1 /*
   2  * Copyright (c) 2009, 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 org.openjdk.jigsaw;
  27 
  28 import java.io.*;
  29 import java.nio.file.attribute.BasicFileAttributes;
  30 import java.nio.file.FileVisitor;
  31 import java.nio.file.FileVisitResult;
  32 import java.nio.file.Path;
  33 import java.util.ArrayList;
  34 import java.util.List;
  35 import java.util.jar.*;
  36 import java.util.zip.*;
  37 import static java.nio.file.StandardCopyOption.COPY_ATTRIBUTES;
  38 import static java.nio.file.StandardCopyOption.REPLACE_EXISTING;
  39 import static java.lang.String.format;
  40 
  41 public final class Files {
  42 
  43     private Files() { }
  44 
  45     // paths are stored with a platform agnostic separator, '/'
  46     static String convertSeparator(String path) {
  47         return path.replace(File.separatorChar, '/');
  48     }
  49 
  50     static String platformSeparator(String path) {
  51         return path.replace('/', File.separatorChar);
  52     }
  53 
  54     static void ensureWriteable(File path) throws IOException {
  55         if (!path.canWrite())
  56             throw new IOException(path + ": is not writeable.");
  57     }
  58 
  59     static String ensureNonAbsolute(String path) throws IOException {
  60         if ((new File(path)).isAbsolute())
  61             throw new IOException("Abolute path instead of relative: " + path);
  62         return path;
  63     }
  64 
  65     static void ensureIsDirectory(File path) throws IOException {
  66         if (!path.exists() || !path.isDirectory())
  67             throw new IOException(path + ": Not a directory");
  68     }
  69 
  70     private static void ensureIsFile(File path)
  71         throws IOException
  72     {
  73         if (!path.exists() || !path.isFile())
  74             throw new IOException(path + ": Not a file");
  75     }
  76 
  77     private static String[] list(File dir)
  78         throws IOException
  79     {
  80         ensureIsDirectory(dir);
  81         String[] fs = dir.list();
  82         if (fs == null)
  83             throw new IOException(dir + ": Cannot list directory contents");
  84         return fs;
  85     }
  86 
  87     private static File[] listFiles(File dir)
  88         throws IOException
  89     {
  90         ensureIsDirectory(dir);
  91         File[] fs = dir.listFiles();
  92         if (fs == null)
  93             throw new IOException(dir + ": Cannot list directory contents");
  94         return fs;
  95     }
  96 
  97     public static void delete(File path)
  98         throws IOException
  99     {
 100         if (!path.delete())
 101             throw new IOException(path + ": Cannot delete");
 102     }
 103 
 104     public static void deleteTree(File dst)
 105         throws IOException
 106     {
 107         File[] fs = listFiles(dst);
 108         for (int i = 0; i < fs.length; i++) {
 109             File f = fs[i];
 110             if (f.isDirectory()) {
 111                 deleteTree(f);
 112             } else {
 113                 delete(f);
 114             }
 115         }
 116         delete(dst);
 117     }
 118 
 119     static List<IOException> deleteTreeUnchecked(Path dir) {
 120         final List<IOException> excs = new ArrayList<>();
 121         try {
 122             java.nio.file.Files.walkFileTree(dir, new FileVisitor<Path>() {
 123                 @Override
 124                 public FileVisitResult preVisitDirectory(Path dir, BasicFileAttributes attrs) {
 125                     return FileVisitResult.CONTINUE;
 126                 }
 127                 @Override
 128                 public FileVisitResult visitFile(Path file, BasicFileAttributes attrs) {
 129                     try {
 130                         java.nio.file.Files.delete(file);
 131                     } catch (IOException x) {
 132                         excs.add(x);
 133                     }
 134                     return FileVisitResult.CONTINUE;
 135                 }
 136                 @Override
 137                 public FileVisitResult postVisitDirectory(Path dir, IOException exc) {
 138                     try {
 139                         java.nio.file.Files.delete(dir);
 140                     } catch (IOException x) {
 141                         excs.add(x);
 142                     }
 143                     return FileVisitResult.CONTINUE;
 144                 }
 145                 @Override
 146                 public FileVisitResult visitFileFailed(Path file, IOException exc) {
 147                     excs.add(new IOException(exc));
 148                     return FileVisitResult.CONTINUE;
 149                 }
 150             });
 151         } catch (IOException x) {
 152             excs.add(x);
 153         }
 154         return excs;
 155     }
 156 
 157     private static void copy(File src, File dst)
 158         throws IOException
 159     {
 160         java.nio.file.Files.copy(src.toPath(), dst.toPath(),
 161                                  COPY_ATTRIBUTES, REPLACE_EXISTING);
 162     }
 163 
 164     public static interface Filter<T> {
 165         public boolean accept(T x) throws IOException;
 166     }
 167 
 168     // src, dst are directories
 169     // src must exist; dst created if it does not yet exist
 170     // Copy files from src to dst, modulo filtering
 171     //
 172     public static void copyTree(File src, File dst, Filter<File> filter)
 173         throws IOException
 174     {
 175         ensureIsDirectory(src);
 176         if (dst.exists()) {
 177             if (!dst.isDirectory())
 178                 delete(dst);
 179         } else if (!dst.mkdirs())
 180             throw new IOException(dst + ": Cannot create directory");
 181         String[] sls = list(src);
 182         for (int i = 0; i < sls.length; i++) {
 183             File sf = new File(src, sls[i]);
 184             if (filter != null && !filter.accept(sf))
 185                 continue;
 186             File df = new File(dst, sls[i]);
 187             if (sf.isDirectory())
 188                 copyTree(sf, df, filter);
 189             else
 190                 copy(sf, df);
 191         }
 192         dst.setLastModified(src.lastModified());
 193     }
 194 
 195     public static void copyTree(File src, File dst)
 196         throws IOException
 197     {
 198         copyTree(src, dst, null);
 199     }
 200 
 201     private static void storeTree(File src, JarOutputStream dst, boolean deflate,
 202                                   Filter<File> filter, String dstPath)
 203         throws IOException
 204     {
 205         ensureIsDirectory(src);
 206         String[] sls = list(src);
 207         for (int i = 0; i < sls.length; i++) {
 208             File sf = new File(src, sls[i]);
 209             if (filter != null && !filter.accept(sf))
 210                 continue;
 211             String dp = (dstPath == null) ? sls[i] : dstPath + "/" + sls[i];
 212             if (sf.isDirectory()) {
 213                 storeTree(sf, dst, deflate, filter, dp);
 214             } else {
 215                 ensureIsFile(sf);
 216                 try (OutputStream out = newOutputStream(dst, deflate, dp)) {
 217                     java.nio.file.Files.copy(sf.toPath(), out);
 218                 }
 219             }
 220         }
 221     }
 222 
 223     public static void storeTree(File src, JarOutputStream dst, boolean deflate,
 224                                  Filter<File> filter)
 225         throws IOException
 226     {
 227         storeTree(src, dst, deflate, filter, null);
 228     }
 229 
 230     public static void storeTree(File src, JarOutputStream dst, boolean deflate)
 231         throws IOException
 232     {
 233         storeTree(src, dst, deflate, null, null);
 234     }
 235 
 236     public static interface Visitor<T> {
 237         public void accept(T x) throws IOException;
 238     }
 239 
 240     public static void walkTree(File src, Visitor<File> visitor)
 241         throws IOException
 242     {
 243         ensureIsDirectory(src);
 244         String[] sls = list(src);
 245         for (int i = 0; i < sls.length; i++) {
 246             File sf = new File(src, sls[i]);
 247             if (sf.isDirectory())
 248                 walkTree(sf, visitor);
 249             else
 250                 visitor.accept(sf);
 251         }
 252     }
 253 
 254     public static byte[] load(InputStream is, int n)
 255         throws IOException
 256     {
 257         DataInputStream in = new DataInputStream(is);
 258         byte[] bs = new byte[n];
 259         try {
 260             in.readFully(bs);
 261             return bs;
 262         } finally {
 263             in.close();
 264         }
 265     }
 266 
 267     public static byte[] load(File src)
 268         throws IOException
 269     {
 270         FileInputStream fis = new FileInputStream(src);
 271         return load(fis, (int)src.length());
 272     }
 273 
 274     public static void store(byte[] bs, File dst)
 275         throws IOException
 276     {
 277         OutputStream out = new FileOutputStream(dst);
 278         int n = bs.length;
 279         try {
 280             int i = 0;
 281             while (i < n) {
 282                 int d = Math.min(n - i, 8192);
 283                 out.write(bs, i, d);
 284                 i += d;
 285             }
 286         } finally {
 287             out.close();
 288         }
 289     }
 290 
 291     public static void mkdirs(File d, String what)
 292         throws IOException
 293     {
 294         if (!d.mkdirs())
 295             throw new IOException(d + ": Cannot create " + what + " directory");
 296     }
 297 
 298     private static class NonClosingInputStream
 299         extends FilterInputStream
 300     {
 301 
 302         private NonClosingInputStream(InputStream out) {
 303             super(out);
 304         }
 305 
 306         public void close() { }
 307 
 308     }
 309 
 310     public static InputStream nonClosingStream(InputStream out) {
 311         return new NonClosingInputStream(out);
 312     }
 313 
 314     private static class JarEntryOutputStream
 315         extends FilterOutputStream
 316     {
 317 
 318         CRC32 crc;
 319         ByteArrayOutputStream baos;
 320         CheckedOutputStream cos;
 321         JarOutputStream jos;
 322         boolean deflate;
 323         String path;
 324 
 325         private JarEntryOutputStream(JarOutputStream jos,
 326                                      boolean deflate,
 327                                      CRC32 crc,
 328                                      ByteArrayOutputStream baos,
 329                                      CheckedOutputStream cos,
 330                                      String path)
 331         {
 332             super(cos);
 333             this.jos = jos;
 334             this.deflate = deflate;
 335             this.crc = crc;
 336             this.baos = baos;
 337             this.cos = cos;
 338             this.path = path;
 339         }
 340 
 341         public void close() throws IOException {
 342             cos.close();
 343             JarEntry je = new JarEntry(path);
 344             if (deflate) {
 345                 je.setMethod(JarEntry.DEFLATED);
 346             } else {
 347                 je.setMethod(JarEntry.STORED);
 348                 je.setCrc(crc.getValue());
 349                 je.setSize(baos.size());
 350                 je.setCompressedSize(baos.size());
 351             }
 352             jos.putNextEntry(je);
 353             baos.writeTo(jos);
 354             jos.closeEntry();
 355         }
 356 
 357     }
 358 
 359     public static JarEntryOutputStream
 360         newOutputStream(JarOutputStream jos, boolean deflate, String path)
 361     {
 362         // Gee, dac, that zip API sure is broken, isn't it?
 363         CRC32 crc = new CRC32();
 364         ByteArrayOutputStream baos = new ByteArrayOutputStream();
 365         CheckedOutputStream cos = new CheckedOutputStream(baos, crc);
 366         return new JarEntryOutputStream(jos, deflate, crc, baos, cos, path);
 367     }
 368 
 369     public static JarEntryOutputStream
 370         newOutputStream(JarOutputStream jos, String path)
 371     {
 372         return newOutputStream(jos, false, path);
 373     }
 374 }