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