code: 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);
      }
  
    }
  
  }
  