Home | History | Annotate | Download | only in file
      1 /*
      2  * Copyright (c) 2011, Oracle and/or its affiliates. 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.  Oracle designates this
      8  * particular file as subject to the "Classpath" exception as provided
      9  * by Oracle 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 Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
     22  * or visit www.oracle.com if you need additional information or have any
     23  * questions.
     24  */
     25 
     26 package java.nio.file;
     27 
     28 import java.nio.file.attribute.*;
     29 import java.io.InputStream;
     30 import java.io.IOException;
     31 
     32 /**
     33  * Helper class to support copying or moving files when the source and target
     34  * are associated with different providers.
     35  */
     36 
     37 class CopyMoveHelper {
     38     private CopyMoveHelper() { }
     39 
     40     /**
     41      * Parses the arguments for a file copy operation.
     42      */
     43     private static class CopyOptions {
     44         boolean replaceExisting = false;
     45         boolean copyAttributes = false;
     46         boolean followLinks = true;
     47 
     48         private CopyOptions() { }
     49 
     50         static CopyOptions parse(CopyOption... options) {
     51             CopyOptions result = new CopyOptions();
     52             for (CopyOption option: options) {
     53                 if (option == StandardCopyOption.REPLACE_EXISTING) {
     54                     result.replaceExisting = true;
     55                     continue;
     56                 }
     57                 if (option == LinkOption.NOFOLLOW_LINKS) {
     58                     result.followLinks = false;
     59                     continue;
     60                 }
     61                 if (option == StandardCopyOption.COPY_ATTRIBUTES) {
     62                     result.copyAttributes = true;
     63                     continue;
     64                 }
     65                 if (option == null)
     66                     throw new NullPointerException();
     67                 throw new UnsupportedOperationException("'" + option +
     68                     "' is not a recognized copy option");
     69             }
     70             return result;
     71         }
     72     }
     73 
     74     /**
     75      * Converts the given array of options for moving a file to options suitable
     76      * for copying the file when a move is implemented as copy + delete.
     77      */
     78     private static CopyOption[] convertMoveToCopyOptions(CopyOption... options)
     79         throws AtomicMoveNotSupportedException
     80     {
     81         int len = options.length;
     82         CopyOption[] newOptions = new CopyOption[len+2];
     83         for (int i=0; i<len; i++) {
     84             CopyOption option = options[i];
     85             if (option == StandardCopyOption.ATOMIC_MOVE) {
     86                 throw new AtomicMoveNotSupportedException(null, null,
     87                     "Atomic move between providers is not supported");
     88             }
     89             newOptions[i] = option;
     90         }
     91         newOptions[len] = LinkOption.NOFOLLOW_LINKS;
     92         newOptions[len+1] = StandardCopyOption.COPY_ATTRIBUTES;
     93         return newOptions;
     94     }
     95 
     96     /**
     97      * Simple copy for use when source and target are associated with different
     98      * providers
     99      */
    100     static void copyToForeignTarget(Path source, Path target,
    101                                     CopyOption... options)
    102         throws IOException
    103     {
    104         CopyOptions opts = CopyOptions.parse(options);
    105         LinkOption[] linkOptions = (opts.followLinks) ? new LinkOption[0] :
    106             new LinkOption[] { LinkOption.NOFOLLOW_LINKS };
    107 
    108         // attributes of source file
    109         BasicFileAttributes attrs = Files.readAttributes(source,
    110                                                          BasicFileAttributes.class,
    111                                                          linkOptions);
    112         if (attrs.isSymbolicLink())
    113             throw new IOException("Copying of symbolic links not supported");
    114 
    115         // delete target if it exists and REPLACE_EXISTING is specified
    116         if (opts.replaceExisting) {
    117             Files.deleteIfExists(target);
    118         } else if (Files.exists(target))
    119             throw new FileAlreadyExistsException(target.toString());
    120 
    121         // create directory or copy file
    122         if (attrs.isDirectory()) {
    123             Files.createDirectory(target);
    124         } else {
    125             try (InputStream in = Files.newInputStream(source)) {
    126                 Files.copy(in, target);
    127             }
    128         }
    129 
    130         // copy basic attributes to target
    131         if (opts.copyAttributes) {
    132             BasicFileAttributeView view =
    133                 Files.getFileAttributeView(target, BasicFileAttributeView.class);
    134             try {
    135                 view.setTimes(attrs.lastModifiedTime(),
    136                               attrs.lastAccessTime(),
    137                               attrs.creationTime());
    138             } catch (Throwable x) {
    139                 // rollback
    140                 try {
    141                     Files.delete(target);
    142                 } catch (Throwable suppressed) {
    143                     x.addSuppressed(suppressed);
    144                 }
    145                 throw x;
    146             }
    147         }
    148     }
    149 
    150     /**
    151      * Simple move implements as copy+delete for use when source and target are
    152      * associated with different providers
    153      */
    154     static void moveToForeignTarget(Path source, Path target,
    155                                     CopyOption... options) throws IOException
    156     {
    157         copyToForeignTarget(source, target, convertMoveToCopyOptions(options));
    158         Files.delete(source);
    159     }
    160 }
    161