package com.beijunyi.parallelgit.filesystem.io; import java.io.IOException; import java.nio.channels.SeekableByteChannel; import java.nio.file.*; import java.nio.file.attribute.FileAttribute; import java.nio.file.attribute.FileAttributeView; import java.util.Collection; import java.util.Set; import javax.annotation.Nonnull; import javax.annotation.Nullable; import com.beijunyi.parallelgit.filesystem.GitPath; import com.beijunyi.parallelgit.filesystem.utils.FileAttributeReader; import static com.beijunyi.parallelgit.filesystem.io.FileNode.newFile; import static java.nio.file.AccessMode.EXECUTE; import static java.nio.file.StandardCopyOption.REPLACE_EXISTING; import static java.nio.file.StandardOpenOption.*; public final class GfsIO { @Nonnull private static GitPath getParent(GitPath child) { if(child.isRoot()) throw new IllegalArgumentException(child.toString()); GitPath parent = child.getParent(); if(parent == null) throw new IllegalStateException(child.toString()); return parent; } @Nullable private static Node findNode(GitPath path) throws IOException { if(!path.isAbsolute()) throw new IllegalArgumentException(path.toString()); Node current = path.getFileStore().getRoot(); for(int i = 0; i < path.getNameCount(); i++) { GitPath name = path.getName(i); if(current instanceof DirectoryNode) current = ((DirectoryNode) current).getChild(name.toString()); else return null; } return current; } @Nonnull private static Node getNode(GitPath path) throws IOException { Node node = findNode(path); if(node == null) throw new NoSuchFileException(path.toString()); return node; } @Nonnull private static FileNode asFile(@Nullable Node node, GitPath path) throws NoSuchFileException, AccessDeniedException { if(node == null) throw new NoSuchFileException(path.toString()); if(node instanceof FileNode) return (FileNode) node; throw new AccessDeniedException(path.toString()); } @Nonnull static FileNode findFile(GitPath file) throws IOException { return asFile(findNode(file), file); } @Nonnull private static DirectoryNode asDirectory(@Nullable Node node, GitPath path) throws NotDirectoryException { if(node instanceof DirectoryNode) return (DirectoryNode) node; throw new NotDirectoryException(path.toString()); } @Nonnull private static DirectoryNode findDirectory(GitPath dir) throws IOException { return asDirectory(findNode(dir), dir); } @Nonnull private static String getFileName(GitPath path) throws IOException { GitPath name = path.getFileName(); assert name != null; return name.toString(); } @Nonnull public static SeekableByteChannel newByteChannel(GitPath file, Set<? extends OpenOption> options, Collection<? extends FileAttribute> attrs) throws IOException { if(file.isRoot()) throw new AccessDeniedException(file.toString()); FileNode node; if(options.contains(CREATE) || options.contains(CREATE_NEW)) { DirectoryNode parent = findDirectory(getParent(file)); String name = getFileName(file); if(options.contains(CREATE_NEW) || !parent.hasChild(name)) { node = newFile(FileAttributeReader.read(attrs).isExecutable(), parent); if(!parent.addChild(name, node, false)) throw new FileAlreadyExistsException(file.toString()); } else { node = asFile(parent.getChild(name), file); } } else { node = findFile(file); } if (options.contains(WRITE)) { return new GfsSeekableByteChannel(node, options); } else { return new GfsSeekableReadOnlyByteChannel(node, options); } } @Nonnull public static GfsDirectoryStream newDirectoryStream(GitPath dir, @Nullable DirectoryStream.Filter<? super Path> filter) throws IOException { return new GfsDirectoryStream(findDirectory(dir), dir, filter); } public static void createDirectory(GitPath dir) throws IOException { if(dir.isRoot()) throw new FileAlreadyExistsException(dir.toString()); DirectoryNode parent = findDirectory(getParent(dir)); if(!parent.addChild(getFileName(dir), DirectoryNode.newDirectory(parent), false)) throw new FileAlreadyExistsException(dir.toString()); } public static boolean copy(GitPath source, GitPath target, Set<CopyOption> options) throws IOException { Node sourceNode = getNode(source); if(source.equals(target)) return false; if(target.isRoot()) throw new AccessDeniedException(target.toString()); GitPath targetParent = getParent(target); DirectoryNode targetDirectory = findDirectory(targetParent); Node targetNode = sourceNode.clone(targetDirectory); if(!targetDirectory.addChild(getFileName(target), targetNode, options.contains(REPLACE_EXISTING))) throw new FileAlreadyExistsException(target.toString()); return true; } public static boolean move(GitPath source, GitPath target, Set<CopyOption> options) throws IOException { if(copy(source, target, options)) { delete(source); return true; } return false; } public static void delete(GitPath file) throws IOException { if(file.isRoot()) throw new AccessDeniedException(file.toString()); GitPath parentPath = getParent(file); Node parent = findNode(getParent(file)); if(parent == null || !parent.isDirectory() || !asDirectory(parent, parentPath).removeChild(getFileName(file))) throw new NoSuchFileException(file.toString()); } public static void checkAccess(GitPath path, Set<AccessMode> modes) throws IOException { Node node = getNode(path); if(modes.contains(EXECUTE) && !node.isExecutableFile()) throw new AccessDeniedException(path.toString()); } @Nullable public static <V extends FileAttributeView> V getFileAttributeView(GitPath path, Class<V> type) throws IOException, UnsupportedOperationException { Node node = findNode(path); return node != null ? GfsFileAttributeView.forNode(node, type) : null; } }