jdk/src/share/classes/java/nio/file/spi/AbstractPath.java
changeset 2057 3acf8e5e2ca0
equal deleted inserted replaced
2056:115e09b7a004 2057:3acf8e5e2ca0
       
     1 /*
       
     2  * Copyright 2007-2009 Sun Microsystems, Inc.  All Rights Reserved.
       
     3  * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
       
     4  *
       
     5  * This code is free software; you can redistribute it and/or modify it
       
     6  * under the terms of the GNU General Public License version 2 only, as
       
     7  * published by the Free Software Foundation.  Sun designates this
       
     8  * particular file as subject to the "Classpath" exception as provided
       
     9  * by Sun in the LICENSE file that accompanied this code.
       
    10  *
       
    11  * This code is distributed in the hope that it will be useful, but WITHOUT
       
    12  * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
       
    13  * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
       
    14  * version 2 for more details (a copy is included in the LICENSE file that
       
    15  * accompanied this code).
       
    16  *
       
    17  * You should have received a copy of the GNU General Public License version
       
    18  * 2 along with this work; if not, write to the Free Software Foundation,
       
    19  * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
       
    20  *
       
    21  * Please contact Sun Microsystems, Inc., 4150 Network Circle, Santa Clara,
       
    22  * CA 95054 USA or visit www.sun.com if you need additional information or
       
    23  * have any questions.
       
    24  */
       
    25 
       
    26 package java.nio.file.spi;
       
    27 
       
    28 import java.nio.file.*;
       
    29 import static java.nio.file.StandardOpenOption.*;
       
    30 import java.nio.file.attribute.*;
       
    31 import java.nio.channels.*;
       
    32 import java.nio.ByteBuffer;
       
    33 import java.io.*;
       
    34 import java.util.*;
       
    35 
       
    36 /**
       
    37  * Base implementation class for a {@code Path}.
       
    38  *
       
    39  * <p> This class is intended to be extended by provider implementors. It
       
    40  * implements, or provides default implementations for several of the methods
       
    41  * defined by the {@code Path} class. It implements the {@link #copyTo copyTo}
       
    42  * and {@link #moveTo moveTo} methods for the case that the source and target
       
    43  * are not associated with the same provider.
       
    44  *
       
    45  * @since 1.7
       
    46  */
       
    47 
       
    48 public abstract class AbstractPath extends Path {
       
    49 
       
    50     /**
       
    51      * Initializes a new instance of this class.
       
    52      */
       
    53     protected AbstractPath() { }
       
    54 
       
    55     /**
       
    56      * Deletes the file referenced by this object.
       
    57      *
       
    58      * <p> This method invokes the {@link #delete(boolean) delete(boolean)}
       
    59      * method with a parameter of {@code true}. It may be overridden where
       
    60      * required.
       
    61      *
       
    62      * @throws  NoSuchFileException             {@inheritDoc}
       
    63      * @throws  DirectoryNotEmptyException      {@inheritDoc}
       
    64      * @throws  IOException                     {@inheritDoc}
       
    65      * @throws  SecurityException               {@inheritDoc}
       
    66      */
       
    67     @Override
       
    68     public void delete() throws IOException {
       
    69         delete(true);
       
    70     }
       
    71 
       
    72     /**
       
    73      * Creates a new and empty file, failing if the file already exists.
       
    74      *
       
    75      * <p> This method invokes the {@link #newByteChannel(Set,FileAttribute[])
       
    76      * newByteChannel(Set,FileAttribute...)} method to create the file. It may be
       
    77      * overridden where required.
       
    78      *
       
    79      * @throws  IllegalArgumentException        {@inheritDoc}
       
    80      * @throws  FileAlreadyExistsException      {@inheritDoc}
       
    81      * @throws  IOException                     {@inheritDoc}
       
    82      * @throws  SecurityException               {@inheritDoc}
       
    83      */
       
    84     @Override
       
    85     public Path createFile(FileAttribute<?>... attrs)
       
    86         throws IOException
       
    87     {
       
    88         EnumSet<StandardOpenOption> options = EnumSet.of(CREATE_NEW, WRITE);
       
    89         SeekableByteChannel sbc = newByteChannel(options, attrs);
       
    90         try {
       
    91             sbc.close();
       
    92         } catch (IOException x) {
       
    93             // ignore
       
    94         }
       
    95         return this;
       
    96     }
       
    97 
       
    98     /**
       
    99      * Opens or creates a file, returning a seekable byte channel to access the
       
   100      * file.
       
   101      *
       
   102      * <p> This method invokes the {@link #newByteChannel(Set,FileAttribute[])
       
   103      * newByteChannel(Set,FileAttribute...)} method to open or create the file.
       
   104      * It may be overridden where required.
       
   105      *
       
   106      * @throws  IllegalArgumentException        {@inheritDoc}
       
   107      * @throws  FileAlreadyExistsException      {@inheritDoc}
       
   108      * @throws  IOException                     {@inheritDoc}
       
   109      * @throws  SecurityException               {@inheritDoc}
       
   110      */
       
   111     @Override
       
   112     public SeekableByteChannel newByteChannel(OpenOption... options)
       
   113         throws IOException
       
   114     {
       
   115         Set<OpenOption> set = new HashSet<OpenOption>(options.length);
       
   116         Collections.addAll(set, options);
       
   117         return newByteChannel(set);
       
   118     }
       
   119 
       
   120     /**
       
   121      * Opens the file located by this path for reading, returning an input
       
   122      * stream to read bytes from the file.
       
   123      *
       
   124      * <p> This method returns an {@code InputStream} that is constructed by
       
   125      * invoking the {@link java.nio.channels.Channels#newInputStream
       
   126      * Channels.newInputStream} method. It may be overridden where a more
       
   127      * efficient implementation is available.
       
   128      *
       
   129      * @throws  IOException                     {@inheritDoc}
       
   130      * @throws  SecurityException               {@inheritDoc}
       
   131      */
       
   132     @Override
       
   133     public InputStream newInputStream() throws IOException {
       
   134         return Channels.newInputStream(newByteChannel());
       
   135     }
       
   136 
       
   137     // opts must be modifiable
       
   138     private OutputStream implNewOutputStream(Set<OpenOption> opts,
       
   139                                              FileAttribute<?>... attrs)
       
   140         throws IOException
       
   141     {
       
   142         if (opts.isEmpty()) {
       
   143             opts.add(CREATE);
       
   144             opts.add(TRUNCATE_EXISTING);
       
   145         } else {
       
   146             if (opts.contains(READ))
       
   147                 throw new IllegalArgumentException("READ not allowed");
       
   148         }
       
   149         opts.add(WRITE);
       
   150         return Channels.newOutputStream(newByteChannel(opts, attrs));
       
   151     }
       
   152 
       
   153     /**
       
   154      * Opens or creates the file located by this path for writing, returning an
       
   155      * output stream to write bytes to the file.
       
   156      *
       
   157      * <p> This method returns an {@code OutputStream} that is constructed by
       
   158      * invoking the {@link java.nio.channels.Channels#newOutputStream
       
   159      * Channels.newOutputStream} method. It may be overridden where a more
       
   160      * efficient implementation is available.
       
   161      *
       
   162      * @throws  IllegalArgumentException        {@inheritDoc}
       
   163      * @throws  IOException                     {@inheritDoc}
       
   164      * @throws  SecurityException               {@inheritDoc}
       
   165      */
       
   166     @Override
       
   167     public OutputStream newOutputStream(OpenOption... options) throws IOException {
       
   168         int len = options.length;
       
   169         Set<OpenOption> opts = new HashSet<OpenOption>(len + 3);
       
   170         if (len > 0) {
       
   171             for (OpenOption opt: options) {
       
   172                 opts.add(opt);
       
   173             }
       
   174         }
       
   175         return implNewOutputStream(opts);
       
   176     }
       
   177 
       
   178     /**
       
   179      * Opens or creates the file located by this path for writing, returning an
       
   180      * output stream to write bytes to the file.
       
   181      *
       
   182      * <p> This method returns an {@code OutputStream} that is constructed by
       
   183      * invoking the {@link java.nio.channels.Channels#newOutputStream
       
   184      * Channels.newOutputStream} method. It may be overridden where a more
       
   185      * efficient implementation is available.
       
   186      *
       
   187      * @throws  IllegalArgumentException        {@inheritDoc}
       
   188      * @throws  IOException                     {@inheritDoc}
       
   189      * @throws  SecurityException               {@inheritDoc}
       
   190      */
       
   191     @Override
       
   192     public OutputStream newOutputStream(Set<? extends OpenOption> options,
       
   193                                         FileAttribute<?>... attrs)
       
   194         throws IOException
       
   195     {
       
   196         Set<OpenOption> opts = new HashSet<OpenOption>(options);
       
   197         return implNewOutputStream(opts, attrs);
       
   198     }
       
   199 
       
   200     /**
       
   201      * Opens the directory referenced by this object, returning a {@code
       
   202      * DirectoryStream} to iterate over all entries in the directory.
       
   203      *
       
   204      * <p> This method invokes the {@link
       
   205      * #newDirectoryStream(java.nio.file.DirectoryStream.Filter)
       
   206      * newDirectoryStream(Filter)} method with a filter that accept all entries.
       
   207      * It may be overridden where required.
       
   208      *
       
   209      * @throws  NotDirectoryException           {@inheritDoc}
       
   210      * @throws  IOException                     {@inheritDoc}
       
   211      * @throws  SecurityException               {@inheritDoc}
       
   212      */
       
   213     @Override
       
   214     public DirectoryStream<Path> newDirectoryStream() throws IOException {
       
   215         return newDirectoryStream(acceptAllFilter);
       
   216     }
       
   217     private static final DirectoryStream.Filter<Path> acceptAllFilter =
       
   218         new DirectoryStream.Filter<Path>() {
       
   219             @Override public boolean accept(Path entry) { return true; }
       
   220         };
       
   221 
       
   222     /**
       
   223      * Opens the directory referenced by this object, returning a {@code
       
   224      * DirectoryStream} to iterate over the entries in the directory. The
       
   225      * entries are filtered by matching the {@code String} representation of
       
   226      * their file names against a given pattern.
       
   227      *
       
   228      * <p> This method constructs a {@link PathMatcher} by invoking the
       
   229      * file system's {@link java.nio.file.FileSystem#getPathMatcher
       
   230      * getPathMatcher} method. This method may be overridden where a more
       
   231      * efficient implementation is available.
       
   232      *
       
   233      * @throws  java.util.regex.PatternSyntaxException  {@inheritDoc}
       
   234      * @throws  UnsupportedOperationException   {@inheritDoc}
       
   235      * @throws  NotDirectoryException           {@inheritDoc}
       
   236      * @throws  IOException                     {@inheritDoc}
       
   237      * @throws  SecurityException               {@inheritDoc}
       
   238      */
       
   239     @Override
       
   240     public DirectoryStream<Path> newDirectoryStream(String glob)
       
   241         throws IOException
       
   242     {
       
   243         // avoid creating a matcher if all entries are required.
       
   244         if (glob.equals("*"))
       
   245             return newDirectoryStream();
       
   246 
       
   247         // create a matcher and return a filter that uses it.
       
   248         final PathMatcher matcher = getFileSystem().getPathMatcher("glob:" + glob);
       
   249         DirectoryStream.Filter<Path> filter = new DirectoryStream.Filter<Path>() {
       
   250             @Override
       
   251             public boolean accept(Path entry)  {
       
   252                 return matcher.matches(entry.getName());
       
   253             }
       
   254         };
       
   255         return newDirectoryStream(filter);
       
   256     }
       
   257 
       
   258     /**
       
   259      * Tests whether the file located by this path exists.
       
   260      *
       
   261      * <p> This method invokes the {@link #checkAccess checkAccess} method to
       
   262      * check if the file exists. It may be  overridden where a more efficient
       
   263      * implementation is available.
       
   264      */
       
   265     @Override
       
   266     public boolean exists() {
       
   267         try {
       
   268             checkAccess();
       
   269             return true;
       
   270         } catch (IOException x) {
       
   271             // unable to determine if file exists
       
   272         }
       
   273         return false;
       
   274     }
       
   275 
       
   276     /**
       
   277      * Tests whether the file located by this path does not exist.
       
   278      *
       
   279      * <p> This method invokes the {@link #checkAccess checkAccess} method to
       
   280      * check if the file exists. It may be  overridden where a more efficient
       
   281      * implementation is available.
       
   282      */
       
   283     @Override
       
   284     public boolean notExists() {
       
   285         try {
       
   286             checkAccess();
       
   287             return false;
       
   288         } catch (NoSuchFileException x) {
       
   289             // file confirmed not to exist
       
   290             return true;
       
   291         } catch (IOException x) {
       
   292             return false;
       
   293         }
       
   294     }
       
   295 
       
   296     /**
       
   297      * Registers the file located by this path with a watch service.
       
   298      *
       
   299      * <p> This method invokes the {@link #register(WatchService,WatchEvent.Kind[],WatchEvent.Modifier[])
       
   300      * register(WatchService,WatchEvent.Kind[],WatchEvent.Modifier...)}
       
   301      * method to register the file. It may be  overridden where required.
       
   302      */
       
   303     @Override
       
   304     public WatchKey register(WatchService watcher, WatchEvent.Kind<?>... events)
       
   305         throws IOException
       
   306     {
       
   307         return register(watcher, events, NO_MODIFIERS);
       
   308     }
       
   309     private static final WatchEvent.Modifier[] NO_MODIFIERS = new WatchEvent.Modifier[0];
       
   310 
       
   311     /**
       
   312      * Copy the file located by this path to a target location.
       
   313      *
       
   314      * <p> This method is invoked by the {@link #copyTo copyTo} method for
       
   315      * the case that this {@code Path} and the target {@code Path} are
       
   316      * associated with the same provider.
       
   317      *
       
   318      * @param   target
       
   319      *          The target location
       
   320      * @param   options
       
   321      *          Options specifying how the copy should be done
       
   322      *
       
   323      * @throws  IllegalArgumentException
       
   324      *          If an invalid option is specified
       
   325      * @throws  FileAlreadyExistsException
       
   326      *          The target file exists and cannot be replaced because the
       
   327      *          {@code REPLACE_EXISTING} option is not specified, or the target
       
   328      *          file is a non-empty directory <i>(optional specific exception)</i>
       
   329      * @throws  IOException
       
   330      *          If an I/O error occurs
       
   331      * @throws  SecurityException
       
   332      *          In the case of the default provider, and a security manager is
       
   333      *          installed, the {@link SecurityManager#checkRead(String) checkRead}
       
   334      *          method is invoked to check read access to the source file, the
       
   335      *          {@link SecurityManager#checkWrite(String) checkWrite} is invoked
       
   336      *          to check write access to the target file. If a symbolic link is
       
   337      *          copied the security manager is invoked to check {@link
       
   338      *          LinkPermission}{@code ("symbolic")}.
       
   339      */
       
   340     protected abstract void implCopyTo(Path target, CopyOption... options)
       
   341         throws IOException;
       
   342 
       
   343     /**
       
   344      * Move the file located by this path to a target location.
       
   345      *
       
   346      * <p> This method is invoked by the {@link #moveTo moveTo} method for
       
   347      * the case that this {@code Path} and the target {@code Path} are
       
   348      * associated with the same provider.
       
   349      *
       
   350      * @param   target
       
   351      *          The target location
       
   352      * @param   options
       
   353      *          Options specifying how the move should be done
       
   354      *
       
   355      * @throws  IllegalArgumentException
       
   356      *          If an invalid option is specified
       
   357      * @throws  FileAlreadyExistsException
       
   358      *          The target file exists and cannot be replaced because the
       
   359      *          {@code REPLACE_EXISTING} option is not specified, or the target
       
   360      *          file is a non-empty directory
       
   361      * @throws  AtomicMoveNotSupportedException
       
   362      *          The options array contains the {@code ATOMIC_MOVE} option but
       
   363      *          the file cannot be moved as an atomic file system operation.
       
   364      * @throws  IOException
       
   365      *          If an I/O error occurs
       
   366      * @throws  SecurityException
       
   367      *          In the case of the default provider, and a security manager is
       
   368      *          installed, the {@link SecurityManager#checkWrite(String) checkWrite}
       
   369      *          method is invoked to check write access to both the source and
       
   370      *          target file.
       
   371      */
       
   372     protected abstract void implMoveTo(Path target, CopyOption... options)
       
   373         throws IOException;
       
   374 
       
   375     /**
       
   376      * Copy the file located by this path to a target location.
       
   377      *
       
   378      * <p> If this path is associated with the same {@link FileSystemProvider
       
   379      * provider} as the {@code target} then the {@link #implCopyTo implCopyTo}
       
   380      * method is invoked to copy the file. Otherwise, this method attempts to
       
   381      * copy the file to the target location in a manner that may be less
       
   382      * efficient than would be the case that target is associated with the same
       
   383      * provider as this path.
       
   384      *
       
   385      * @throws  IllegalArgumentException            {@inheritDoc}
       
   386      * @throws  FileAlreadyExistsException          {@inheritDoc}
       
   387      * @throws  IOException                         {@inheritDoc}
       
   388      * @throws  SecurityException                   {@inheritDoc}
       
   389      */
       
   390     @Override
       
   391     public final Path copyTo(Path target, CopyOption... options)
       
   392         throws IOException
       
   393     {
       
   394         if ((getFileSystem().provider() == target.getFileSystem().provider())) {
       
   395             implCopyTo(target, options);
       
   396         } else {
       
   397             xProviderCopyTo(target, options);
       
   398         }
       
   399         return target;
       
   400     }
       
   401 
       
   402     /**
       
   403      * Move or rename the file located by this path to a target location.
       
   404      *
       
   405      * <p> If this path is associated with the same {@link FileSystemProvider
       
   406      * provider} as the {@code target} then the {@link #implCopyTo implMoveTo}
       
   407      * method is invoked to move the file. Otherwise, this method attempts to
       
   408      * copy the file to the target location and delete the source file. This
       
   409      * implementation may be less efficient than would be the case that
       
   410      * target is associated with the same provider as this path.
       
   411      *
       
   412      * @throws  IllegalArgumentException            {@inheritDoc}
       
   413      * @throws  FileAlreadyExistsException          {@inheritDoc}
       
   414      * @throws  IOException                         {@inheritDoc}
       
   415      * @throws  SecurityException                   {@inheritDoc}
       
   416      */
       
   417     @Override
       
   418     public final Path moveTo(Path target, CopyOption... options)
       
   419         throws IOException
       
   420     {
       
   421         if ((getFileSystem().provider() == target.getFileSystem().provider())) {
       
   422             implMoveTo(target, options);
       
   423         } else {
       
   424             // different providers so copy + delete
       
   425             xProviderCopyTo(target, convertMoveToCopyOptions(options));
       
   426             delete(false);
       
   427         }
       
   428         return target;
       
   429     }
       
   430 
       
   431     /**
       
   432      * Converts the given array of options for moving a file to options suitable
       
   433      * for copying the file when a move is implemented as copy + delete.
       
   434      */
       
   435     private static CopyOption[] convertMoveToCopyOptions(CopyOption... options)
       
   436         throws AtomicMoveNotSupportedException
       
   437     {
       
   438         int len = options.length;
       
   439         CopyOption[] newOptions = new CopyOption[len+2];
       
   440         for (int i=0; i<len; i++) {
       
   441             CopyOption option = options[i];
       
   442             if (option == StandardCopyOption.ATOMIC_MOVE) {
       
   443                 throw new AtomicMoveNotSupportedException(null, null,
       
   444                     "Atomic move between providers is not supported");
       
   445             }
       
   446             newOptions[i] = option;
       
   447         }
       
   448         newOptions[len] = LinkOption.NOFOLLOW_LINKS;
       
   449         newOptions[len+1] = StandardCopyOption.COPY_ATTRIBUTES;
       
   450         return newOptions;
       
   451     }
       
   452 
       
   453     /**
       
   454      * Parses the arguments for a file copy operation.
       
   455      */
       
   456     private static class CopyOptions {
       
   457         boolean replaceExisting = false;
       
   458         boolean copyAttributes = false;
       
   459         boolean followLinks = true;
       
   460 
       
   461         private CopyOptions() { }
       
   462 
       
   463         static CopyOptions parse(CopyOption... options) {
       
   464             CopyOptions result = new CopyOptions();
       
   465             for (CopyOption option: options) {
       
   466                 if (option == StandardCopyOption.REPLACE_EXISTING) {
       
   467                     result.replaceExisting = true;
       
   468                     continue;
       
   469                 }
       
   470                 if (option == LinkOption.NOFOLLOW_LINKS) {
       
   471                     result.followLinks = false;
       
   472                     continue;
       
   473                 }
       
   474                 if (option == StandardCopyOption.COPY_ATTRIBUTES) {
       
   475                     result.copyAttributes = true;
       
   476                     continue;
       
   477                 }
       
   478                 if (option == null)
       
   479                     throw new NullPointerException();
       
   480                 throw new IllegalArgumentException("'" + option +
       
   481                     "' is not a valid copy option");
       
   482             }
       
   483             return result;
       
   484         }
       
   485     }
       
   486 
       
   487     /**
       
   488      * Simple cross-provider copy where the target is a Path.
       
   489      */
       
   490     private void xProviderCopyTo(Path target, CopyOption... options)
       
   491         throws IOException
       
   492     {
       
   493         CopyOptions opts = CopyOptions.parse(options);
       
   494         LinkOption[] linkOptions = (opts.followLinks) ? new LinkOption[0] :
       
   495             new LinkOption[] { LinkOption.NOFOLLOW_LINKS };
       
   496 
       
   497         // attributes of source file
       
   498         BasicFileAttributes attrs = Attributes
       
   499             .readBasicFileAttributes(this, linkOptions);
       
   500         if (attrs.isSymbolicLink())
       
   501             throw new IOException("Copying of symbolic links not supported");
       
   502 
       
   503         // delete target file
       
   504         if (opts.replaceExisting)
       
   505             target.delete(false);
       
   506 
       
   507         // create directory or file
       
   508         if (attrs.isDirectory()) {
       
   509             target.createDirectory();
       
   510         } else {
       
   511             xProviderCopyRegularFileTo(target);
       
   512         }
       
   513 
       
   514         // copy basic attributes to target
       
   515         if (opts.copyAttributes) {
       
   516             BasicFileAttributeView view = target
       
   517                 .getFileAttributeView(BasicFileAttributeView.class, linkOptions);
       
   518             try {
       
   519                 view.setTimes(attrs.lastModifiedTime(),
       
   520                               attrs.lastAccessTime(),
       
   521                               attrs.creationTime(),
       
   522                               attrs.resolution());
       
   523             } catch (IOException x) {
       
   524                 // rollback
       
   525                 try {
       
   526                     target.delete(false);
       
   527                 } catch (IOException ignore) { }
       
   528                 throw x;
       
   529             }
       
   530         }
       
   531     }
       
   532 
       
   533 
       
   534    /**
       
   535      * Simple copy of regular file to a target file that exists.
       
   536      */
       
   537     private void xProviderCopyRegularFileTo(FileRef target)
       
   538         throws IOException
       
   539     {
       
   540         ReadableByteChannel rbc = newByteChannel();
       
   541         try {
       
   542             // open target file for writing
       
   543             SeekableByteChannel sbc = target.newByteChannel(CREATE, WRITE);
       
   544 
       
   545             // simple copy loop
       
   546             try {
       
   547                 ByteBuffer buf = ByteBuffer.wrap(new byte[8192]);
       
   548                 int n = 0;
       
   549                 for (;;) {
       
   550                     n = rbc.read(buf);
       
   551                     if (n < 0)
       
   552                         break;
       
   553                     assert n > 0;
       
   554                     buf.flip();
       
   555                     while (buf.hasRemaining()) {
       
   556                         sbc.write(buf);
       
   557                     }
       
   558                     buf.rewind();
       
   559                 }
       
   560 
       
   561             } finally {
       
   562                 sbc.close();
       
   563             }
       
   564         } finally {
       
   565             rbc.close();
       
   566         }
       
   567     }
       
   568 }