package net.minecraftforkage.instsetup; import java.io.BufferedInputStream; import java.io.BufferedOutputStream; import java.io.Closeable; import java.io.File; import java.io.IOException; import java.io.InputStream; import java.io.OutputStream; import java.nio.file.FileSystem; import java.nio.file.FileVisitResult; import java.nio.file.Files; import java.nio.file.Path; import java.nio.file.SimpleFileVisitor; import java.nio.file.StandardOpenOption; import java.nio.file.attribute.BasicFileAttributes; import java.util.Collections; import java.util.HashSet; import java.util.Iterator; import java.util.Set; class ZipFileSystemAdapter extends AbstractZipFile implements Closeable { final FileSystem fs; final Path root; public ZipFileSystemAdapter(FileSystem fs) throws IOException { this.fs = fs; try { Iterator<Path> roots = fs.getRootDirectories().iterator(); if(!roots.hasNext()) throw new RuntimeException("filesystem has no root directories"); this.root = roots.next(); if(roots.hasNext()) throw new RuntimeException("filesystem has more than one root directory"); filenames = new HashSet<>(); Files.walkFileTree(root, new SimpleFileVisitor<Path>() { @Override public FileVisitResult preVisitDirectory(Path dir, BasicFileAttributes attrs) throws IOException { filenames.add(nioPathToString(dir)+"/"); return FileVisitResult.CONTINUE; } @Override public FileVisitResult visitFile(Path file, BasicFileAttributes attrs) throws IOException { filenames.add(nioPathToString(file)); return FileVisitResult.CONTINUE; } }); filenamesUnmod = Collections.unmodifiableSet(filenames); } catch(Throwable e) { try { fs.close(); } catch(Throwable e2) { e.addSuppressed(e2); } throw e; } } @Override public synchronized void close() throws IOException { fs.close(); } Set<String> filenames; Set<String> filenamesUnmod; private String nioPathToString(Path path) { return root.relativize(path).toString().replace(File.separator, "/"); } private Path getNioPath(String path) { Path p = fs.getPath(File.separator + path.replace("/", File.separator)); String canonical = nioPathToString(p); if(!canonical.equals(path) && !(canonical+"/").equals(path)) throw new IllegalArgumentException("not canonical path format: " + path + " (should be " + canonical + ")"); return p; } @Override public Iterable<String> getFileNames() throws IOException { return filenamesUnmod; } @Override public synchronized boolean doesPathExist(String path) throws IOException { return filenames.contains(path); } @Override public synchronized InputStream read(String path) throws IOException { if(path.endsWith("/")) throw new IllegalArgumentException("can't read a directory: " + path); return new BufferedInputStream(Files.newInputStream(getNioPath(path), StandardOpenOption.READ)); } @Override public synchronized OutputStream write(String path) throws IOException { if(path.endsWith("/")) throw new IllegalArgumentException("can't write a directory: " + path); Path path2 = getNioPath(path); filenames.add(nioPathToString(path2)); return new BufferedOutputStream(Files.newOutputStream(path2, StandardOpenOption.CREATE, StandardOpenOption.WRITE)); } @Override public synchronized void delete(String path) throws IOException { Path path2 = getNioPath(path); if(path.endsWith("/")) { if(!Files.isDirectory(path2)) throw new IllegalArgumentException("Not a directory: " + path); } else { if(Files.isDirectory(path2)) throw new IllegalArgumentException("Not a file: " + path); } Files.delete(path2); if(path.endsWith("/")) filenames.remove(nioPathToString(path2) + "/"); else filenames.remove(nioPathToString(path2)); } @Override public synchronized void createDirectory(String path) throws IOException { if(!path.endsWith("/")) throw new IllegalArgumentException("Not a directory path: " + path); createDirectory(getNioPath(path)); } private void createDirectory(Path path) throws IOException { if(Files.isDirectory(path)) return; Path parent = path.getParent(); if(parent != null) createDirectory(parent); Files.createDirectory(path); filenames.add(nioPathToString(path) + "/"); } }