package com.beijunyi.parallelgit.filesystem.io; import java.io.IOException; import java.nio.file.attribute.*; import java.util.*; import javax.annotation.Nonnull; import javax.annotation.Nullable; import org.eclipse.jgit.lib.FileMode; import static java.nio.file.attribute.FileTime.fromMillis; import static java.nio.file.attribute.PosixFilePermission.*; import static java.util.Arrays.asList; import static java.util.Collections.*; import static org.eclipse.jgit.lib.FileMode.*; public abstract class GfsFileAttributeView implements FileAttributeView { public static final String SIZE_NAME = "size"; public static final String CREATION_TIME_NAME = "creationTime"; public static final String LAST_ACCESS_TIME_NAME = "lastAccessTime"; public static final String LAST_MODIFIED_TIME_NAME = "lastModifiedTime"; public static final String FILE_KEY_NAME = "fileKey"; public static final String IS_DIRECTORY_NAME = "isSubtree"; public static final String IS_REGULAR_FILE_NAME = "isRegularFile"; public static final String IS_SYMBOLIC_LINK_NAME = "isSymbolicLink"; public static final String IS_OTHER_NAME = "isOther"; public static final String PERMISSIONS_NAME = "permissions"; public static final String OWNER_NAME = "owner"; public static final String GROUP_NAME = "group"; public static final String IS_NEW = "isNew"; public static final String IS_MODIFIED = "isModified"; public static final String OBJECT_ID = "objectId"; public static final String FILE_MODE = "fileMode"; protected final Node node; protected GfsFileAttributeView(Node node) { this.node = node; } @Nonnull static <V extends FileAttributeView> V forNode(Node node, Class<V> type) throws UnsupportedOperationException { if(type.isAssignableFrom(GfsFileAttributeView.Basic.class)) return type.cast(new GfsFileAttributeView.Basic(node)); if(type.isAssignableFrom(GfsFileAttributeView.Posix.class)) return type.cast(new GfsFileAttributeView.Posix(node)); if(type.isAssignableFrom(GfsFileAttributeView.Git.class)) return type.cast(new GfsFileAttributeView.Git(node)); throw new UnsupportedOperationException(type.getName()); } @Nonnull public abstract Map<String, Object> readAttributes(Collection<String> attributes) throws IOException; public static class Basic extends GfsFileAttributeView implements BasicFileAttributeView { public static final String BASIC_VIEW = "basic"; public static final Set<String> BASIC_KEYS = keys(); private static final FileTime EPOCH = fromMillis(0); protected Basic(Node node) { super(node); } @Nonnull @Override public String name() { return BASIC_VIEW; } @Nonnull @Override public BasicFileAttributes readAttributes() throws IOException { return new GfsFileAttributes.Basic(this); } @Override public void setTimes(FileTime lastModifiedTime, FileTime lastAccessTime, FileTime createTime) { throw new UnsupportedOperationException(); } @Nonnull @Override public Map<String, Object> readAttributes(Collection<String> keys) throws IOException { Map<String, Object> result = new HashMap<>(); for(String key : keys) { switch(key) { case SIZE_NAME: result.put(key, node.getSize()); break; case CREATION_TIME_NAME: result.put(key, EPOCH); break; case LAST_ACCESS_TIME_NAME: result.put(key, EPOCH); break; case LAST_MODIFIED_TIME_NAME: result.put(key, EPOCH); break; case FILE_KEY_NAME: result.put(key, null); break; case IS_DIRECTORY_NAME: result.put(key, node.isDirectory()); break; case IS_REGULAR_FILE_NAME: result.put(key, node.isRegularFile()); break; case IS_SYMBOLIC_LINK_NAME: result.put(key, node.isSymbolicLink()); break; case IS_OTHER_NAME: result.put(key, false); break; default: throw new UnsupportedOperationException("Attribute \"" + key + "\" is not supported"); } } return unmodifiableMap(result); } @Nonnull private static Set<String> keys() { Set<String> ret = new HashSet<>(asList( SIZE_NAME, CREATION_TIME_NAME, LAST_ACCESS_TIME_NAME, LAST_MODIFIED_TIME_NAME, FILE_KEY_NAME, IS_DIRECTORY_NAME, IS_REGULAR_FILE_NAME, IS_SYMBOLIC_LINK_NAME, IS_OTHER_NAME )); return unmodifiableSet(ret); } } public static class Posix extends Basic implements PosixFileAttributeView { public static final String POSIX_VIEW = "posix"; public static final Set<String> POSIX_KEYS = keys(); private static final Collection<PosixFilePermission> DEFAULT_PERMISSIONS = defaultPermissions(); protected Posix(Node node) { super(node); } @Nonnull @Override public String name() { return POSIX_VIEW; } @Nonnull @Override public PosixFileAttributes readAttributes() throws IOException { return new GfsFileAttributes.Posix(this); } @Nonnull public Set<PosixFilePermission> getPermissions() throws IOException { Set<PosixFilePermission> perms = new HashSet<>(DEFAULT_PERMISSIONS); if(node.isExecutableFile()) perms.add(OWNER_EXECUTE); return unmodifiableSet(perms); } @Override public void setPermissions(Set<PosixFilePermission> perms) throws IOException { FileMode mode = perms.contains(OWNER_EXECUTE) ? EXECUTABLE_FILE : REGULAR_FILE; node.setMode(mode); } @Override public void setGroup(GroupPrincipal group) throws IOException { throw new UnsupportedOperationException(); } @Nullable @Override public UserPrincipal getOwner() throws IOException { return null; } @Override public void setOwner(UserPrincipal owner) throws IOException { throw new UnsupportedOperationException(); } @Nonnull @Override public Map<String, Object> readAttributes(Collection<String> keys) throws IOException { Set<String> basicKeys = new HashSet<>(keys); basicKeys.retainAll(Basic.BASIC_KEYS); Map<String, Object> result = new HashMap<>(super.readAttributes(basicKeys)); Set<String> remainKeys = new HashSet<>(keys); remainKeys.removeAll(result.keySet()); for(String key : remainKeys) { switch(key) { case PERMISSIONS_NAME: result.put(key, getPermissions()); break; case OWNER_NAME: result.put(key, getOwner()); break; case GROUP_NAME: result.put(key, null); break; default: throw new UnsupportedOperationException("Attribute \"" + key + "\" is not supported"); } } return unmodifiableMap(result); } @Nonnull private static Collection<PosixFilePermission> defaultPermissions() { return unmodifiableCollection(asList(OWNER_READ, OWNER_WRITE)); } @Nonnull private static Set<String> keys() { Set<String> ret = new HashSet<>(); ret.addAll(Basic.BASIC_KEYS); ret.addAll(asList(PERMISSIONS_NAME, OWNER_NAME, GROUP_NAME)); return unmodifiableSet(ret); } } public static class Git extends Posix implements GitFileAttributeView { public static final String GIT_VIEW = "git"; public static final Set<String> GIT_KEYS = Git.keys(); protected Git(Node node) { super(node); } @Nonnull @Override public String name() { return GIT_VIEW; } @Override public void setFileMode(FileMode mode) { node.setMode(mode); } @Nonnull @Override public GitFileAttributes readAttributes() throws IOException { return new GfsFileAttributes.Git(this); } @Nonnull @Override public Map<String, Object> readAttributes(Collection<String> keys) throws IOException { Set<String> posixKeys = new HashSet<>(keys); posixKeys.retainAll(Posix.POSIX_KEYS); Map<String, Object> result = new HashMap<>(super.readAttributes(posixKeys)); Set<String> remainKeys = new HashSet<>(keys); remainKeys.removeAll(result.keySet()); for(String key : remainKeys) { switch(key) { case IS_NEW: result.put(key, node.isNew()); break; case IS_MODIFIED: result.put(key, node.isModified()); break; case OBJECT_ID: result.put(key, node.getObjectId(false)); break; case FILE_MODE: result.put(key, node.getMode()); break; default: throw new UnsupportedOperationException(key); } } return unmodifiableMap(result); } @Nonnull private static Set<String> keys() { Set<String> ret = new HashSet<>(); ret.addAll(Posix.POSIX_KEYS); ret.addAll(asList(IS_NEW, IS_MODIFIED, OBJECT_ID, FILE_MODE)); return unmodifiableSet(ret); } } }