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