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, OutputStream out)
 111         throws IOException
 112     {
 113         ensureIsFile(src);
 114         byte[] buf = new byte[8192];
 115         FileInputStream in = new FileInputStream(src);
 116         try {
 117             try {
 118                 int n;
 119                 while ((n = in.read(buf)) > 0) {
 120                     out.write(buf, 0, n);
 121                 }
 122             } finally {
 123                 out.close();
 124             }
 125         } finally {
 126             in.close();
 127         }
 128     }
 129 
 130     private static void copy(File src, File dst)
 131         throws IOException
 132     {
 133         if (dst.exists())
 134             ensureIsFile(dst);
 135         copy(src, new FileOutputStream(dst));
 136         dst.setLastModified(src.lastModified());
 137         if (src.canExecute())
 138             dst.setExecutable(true, false);
 139     }
 140 
 141     public static interface Filter<T> {
 142         public boolean accept(T x) throws IOException;
 143     }
 144 
 145     // src, dst are directories
 146     // src must exist; dst created if it does not yet exist
 147     // Copy files from src to dst, modulo filtering
 148     //
 149     public static void copyTree(File src, File dst, Filter<File> filter)
 150         throws IOException
 151     {
 152         ensureIsDirectory(src);
 153         if (dst.exists()) {
 154             if (!dst.isDirectory())
 155                 delete(dst);
 156         } else if (!dst.mkdirs())
 157             throw new IOException(dst + ": Cannot create directory");
 158         String[] sls = list(src);
 159         for (int i = 0; i < sls.length; i++) {
 160             File sf = new File(src, sls[i]);
 161             if (filter != null && !filter.accept(sf))
 162                 continue;
 163             File df = new File(dst, sls[i]);
 164             if (sf.isDirectory())
 165                 copyTree(sf, df, filter);
 166             else
 167                 copy(sf, df);
 168         }
 169         dst.setLastModified(src.lastModified());
 170     }
 171 
 172     public static void copyTree(File src, File dst)
 173         throws IOException
 174     {
 175         copyTree(src, dst, null);
 176     }
 177 
 178     private static void storeTree(File src, JarOutputStream dst, boolean deflate,
 179                                   Filter<File> filter, String dstPath)
 180         throws IOException
 181     {
 182         ensureIsDirectory(src);
 183         String[] sls = list(src);
 184         for (int i = 0; i < sls.length; i++) {
 185             File sf = new File(src, sls[i]);
 186             if (filter != null && !filter.accept(sf))
 187                 continue;
 188             String dp = (dstPath == null) ? sls[i] : dstPath + "/" + sls[i];
 189             if (sf.isDirectory()) {
 190                 storeTree(sf, dst, deflate, filter, dp);
 191             } else {
 192                 copy(sf, newOutputStream(dst, deflate, dp));
 193             }
 194         }
 195     }
 196 
 197     public static void storeTree(File src, JarOutputStream dst, boolean deflate,
 198                                  Filter<File> filter)
 199         throws IOException
 200     {
 201         storeTree(src, dst, deflate, filter, null);
 202     }
 203 
 204     public static void storeTree(File src, JarOutputStream dst, boolean deflate)
 205         throws IOException
 206     {
 207         storeTree(src, dst, deflate, null, null);
 208     }
 209 
 210     public static interface Visitor<T> {
 211         public void accept(T x) throws IOException;
 212     }
 213 
 214     public static void walkTree(File src, Visitor<File> visitor)
 215         throws IOException
 216     {
 217         ensureIsDirectory(src);
 218         String[] sls = list(src);
 219         for (int i = 0; i < sls.length; i++) {
 220             File sf = new File(src, sls[i]);
 221             if (sf.isDirectory())
 222                 walkTree(sf, visitor);
 223             else
 224                 visitor.accept(sf);
 225         }
 226     }
 227 
 228     public static byte[] load(InputStream is, int n)
 229         throws IOException
 230     {
 231         DataInputStream in = new DataInputStream(is);
 232         byte[] bs = new byte[n];
 233         try {
 234             in.readFully(bs);
 235             return bs;
 236         } finally {
 237             in.close();
 238         }
 239     }
 240 
 241     public static byte[] load(File src)
 242         throws IOException
 243     {
 244         FileInputStream fis = new FileInputStream(src);
 245         return load(fis, (int)src.length());
 246     }
 247 
 248     public static void store(byte[] bs, File dst)
 249         throws IOException
 250     {
 251         OutputStream out = new FileOutputStream(dst);
 252         int n = bs.length;
 253         try {
 254             int i = 0;
 255             while (i < n) {
 256                 int d = Math.min(n - i, 8192);
 257                 out.write(bs, i, d);
 258                 i += d;
 259             }
 260         } finally {
 261             out.close();
 262         }
 263     }
 264 
 265     public static void mkdirs(File d, String what)
 266         throws IOException
 267     {
 268         if (!d.mkdirs())
 269             throw new IOException(d + ": Cannot create " + what + " directory");
 270     }
 271 
 272     private static class NonClosingInputStream
 273         extends FilterInputStream
 274     {
 275 
 276         private NonClosingInputStream(InputStream out) {
 277             super(out);
 278         }
 279 
 280         public void close() { }
 281 
 282     }
 283 
 284     public static InputStream nonClosingStream(InputStream out) {
 285         return new NonClosingInputStream(out);
 286     }
 287 
 288     private static class JarEntryOutputStream
 289         extends FilterOutputStream
 290     {
 291 
 292         CRC32 crc;
 293         ByteArrayOutputStream baos;
 294         CheckedOutputStream cos;
 295         JarOutputStream jos;
 296         boolean deflate;
 297         String path;
 298 
 299         private JarEntryOutputStream(JarOutputStream jos,
 300                                      boolean deflate,
 301                                      CRC32 crc,
 302                                      ByteArrayOutputStream baos,
 303                                      CheckedOutputStream cos,
 304                                      String path)
 305         {
 306             super(cos);
 307             this.jos = jos;
 308             this.deflate = deflate;
 309             this.crc = crc;
 310             this.baos = baos;
 311             this.cos = cos;
 312             this.path = path;
 313         }
 314 
 315         public void close() throws IOException {
 316             cos.close();
 317             JarEntry je = new JarEntry(path);
 318             if (deflate) {
 319                 je.setMethod(JarEntry.DEFLATED);
 320             } else {
 321                 je.setMethod(JarEntry.STORED);
 322                 je.setCrc(crc.getValue());
 323                 je.setSize(baos.size());
 324                 je.setCompressedSize(baos.size());
 325             }
 326             jos.putNextEntry(je);
 327             baos.writeTo(jos);
 328             jos.closeEntry();
 329         }
 330 
 331     }
 332 
 333     public static JarEntryOutputStream
 334         newOutputStream(JarOutputStream jos, boolean deflate, String path)
 335     {
 336         // Gee, dac, that zip API sure is broken, isn't it?
 337         CRC32 crc = new CRC32();
 338         ByteArrayOutputStream baos = new ByteArrayOutputStream();
 339         CheckedOutputStream cos = new CheckedOutputStream(baos, crc);
 340         return new JarEntryOutputStream(jos, deflate, crc, baos, cos, path);
 341     }
 342 
 343     public static JarEntryOutputStream
 344         newOutputStream(JarOutputStream jos, String path)
 345     {
 346         return newOutputStream(jos, false, path);
 347     }
 348 }