Java7Nio2ApiPermissionsStrategy.java

package org.zeroturnaround.zip;

import java.io.File;
import java.lang.reflect.Array;
import java.lang.reflect.Method;
import java.util.HashSet;
import java.util.Set;

/**
 * ZTFilePermissionsStrategy which uses Java 7 posix file permissions
 * 
 * @author VIktor Karabut
 */
class Java7Nio2ApiPermissionsStrategy implements ZTFilePermissionsStrategy {
  private final Class<? extends Enum<?>> posixFilePermissionClass;
  private final Class<?> filesClass;
  private final Class<?> pathClass;
  private final Class<? extends Enum<?>> linkOptionClass;
  private final Enum<?>[] linkOptionsArray;
  
  private final Method toPathMethod;
  private final Method setPosixFilePermissionsMethod;
  private final Method getPosixFilePermissionsMethod;
  
  private final Object OWNER_READ;
  private final Object OWNER_WRITE;
  private final Object OWNER_EXECUTE;
  private final Object GROUP_READ;
  private final Object GROUP_WRITE;
  private final Object GROUP_EXECUTE;
  private final Object OTHERS_READ;
  private final Object OTHERS_WRITE;
  private final Object OTHERS_EXECUTE;
  
  
  @SuppressWarnings("unchecked")
  public Java7Nio2ApiPermissionsStrategy() {
    if (!isPosix()) {
      throw new ZipException("File system does not support POSIX file attributes");
    }
    
    posixFilePermissionClass = 
        (Class<? extends Enum<?>>) ZTZipReflectionUtil.getClassForName("java.nio.file.attribute.PosixFilePermission", Enum.class);
    Enum<?>[] constants = posixFilePermissionClass.getEnumConstants();
    OWNER_READ = constants[0];
    OWNER_WRITE = constants[1];
    OWNER_EXECUTE = constants[2];
    GROUP_READ = constants[3];
    GROUP_WRITE = constants[4];
    GROUP_EXECUTE = constants[5];
    OTHERS_READ = constants[6];
    OTHERS_WRITE = constants[7];
    OTHERS_EXECUTE = constants[8];
    
    linkOptionClass = 
        (Class<? extends Enum<?>>) ZTZipReflectionUtil.getClassForName("java.nio.file.LinkOption", Enum.class);
    linkOptionsArray = (Enum<?>[]) Array.newInstance(linkOptionClass, 1);
    linkOptionsArray[0] = (Enum<?>) linkOptionClass.getEnumConstants()[0]; // LinkOption.NOFOLLOW_LINKS;
    
    filesClass = ZTZipReflectionUtil.getClassForName("java.nio.file.Files", Object.class);
    pathClass = ZTZipReflectionUtil.getClassForName("java.nio.file.Path", Object.class);
    
    toPathMethod = ZTZipReflectionUtil.getDeclaredMethod(File.class, "toPath");
    setPosixFilePermissionsMethod = ZTZipReflectionUtil.getDeclaredMethod(filesClass, "setPosixFilePermissions", pathClass, Set.class);
    getPosixFilePermissionsMethod = ZTZipReflectionUtil.getDeclaredMethod(filesClass, "getPosixFilePermissions", pathClass, linkOptionsArray.getClass());
  }

  public ZTFilePermissions getPermissions(File file) {
    ZTFilePermissions permissions = new ZTFilePermissions();
    permissions.setDirectory(file.isDirectory());
    
    Set<?> posixFilePermissions = getPosixFilePermissions(file);
    
    permissions.setOwnerCanRead(   posixFilePermissions.contains(OWNER_READ));
    permissions.setOwnerCanWrite(  posixFilePermissions.contains(OWNER_WRITE));
    permissions.setOwnerCanExecute(posixFilePermissions.contains(OWNER_EXECUTE));
    
    permissions.setGroupCanRead(   posixFilePermissions.contains(GROUP_READ));
    permissions.setGroupCanWrite(  posixFilePermissions.contains(GROUP_WRITE));
    permissions.setGroupCanExecute(posixFilePermissions.contains(GROUP_EXECUTE));
    
    permissions.setOthersCanRead(   posixFilePermissions.contains(OTHERS_READ));
    permissions.setOthersCanWrite(  posixFilePermissions.contains(OTHERS_WRITE));
    permissions.setOthersCanExecute(posixFilePermissions.contains(OTHERS_EXECUTE));
    
    return permissions;
  }

  public void setPermissions(File file, ZTFilePermissions permissions) {
    Set<Object> set = new HashSet<Object>();
    addIf(permissions.isOwnerCanRead(), set, OWNER_READ);
    addIf(permissions.isOwnerCanRead(),   set,OWNER_READ);
    addIf(permissions.isOwnerCanWrite(),  set,OWNER_WRITE);
    addIf(permissions.isOwnerCanExecute(),set,OWNER_EXECUTE);
    
    addIf(permissions.isGroupCanRead(),   set,GROUP_READ);
    addIf(permissions.isGroupCanWrite(),  set,GROUP_WRITE);
    addIf(permissions.isGroupCanExecute(),set,GROUP_EXECUTE);
    
    addIf(permissions.isOthersCanRead(),   set,OTHERS_READ);
    addIf(permissions.isOthersCanWrite(),  set,OTHERS_WRITE);
    addIf(permissions.isOthersCanExecute(),set,OTHERS_EXECUTE);
    
    setPosixFilePermissions(file, set);
  }
  
  private <E> void addIf(boolean condition, Set<E> set, E el) {
    if (condition) {
      set.add(el);
    }
  }
  
  /**
   * Construct java.nio.file.Path object from abstract path.
   * Invokes JDK7 <code>file.toPath()</code> method through reflection.
   * 
   * @param file
   * @return instance of java.nio.file.Path object
   */
  private Object toPath(File file) {
    return ZTZipReflectionUtil.invoke(toPathMethod, file);
  }
  
  // Files.setPosixFilePermissions(file.toPath(), set);
  private void setPosixFilePermissions(File file, Set<?> set) {
    ZTZipReflectionUtil.invoke(setPosixFilePermissionsMethod, null, toPath(file), set);
  }
  
  // Files.getPosixFilePermissions(file.toPath(), new LinkOption[]{ LinkOption.NOFOLLOW_LINKS });
  private Set<?> getPosixFilePermissions(File file) {
    return (Set<?>) ZTZipReflectionUtil.invoke(getPosixFilePermissionsMethod, null, toPath(file), linkOptionsArray);
  }
  
  // FileSystems.getDefault().supportedFileAttrubuteViews().contains("posix");
  private static boolean isPosix() {
    Method getDefaultMethod = ZTZipReflectionUtil.getDeclaredMethod(
        ZTZipReflectionUtil.getClassForName("java.nio.file.FileSystems", Object.class), "getDefault");
    Method supportedFileAttributeViewsMethod = ZTZipReflectionUtil.getDeclaredMethod(
        ZTZipReflectionUtil.getClassForName("java.nio.file.FileSystem", Object.class), "supportedFileAttributeViews");
    
    Object fileSystem = ZTZipReflectionUtil.invoke(getDefaultMethod, null);
    @SuppressWarnings("unchecked")
    Set<String> views = (Set<String>) ZTZipReflectionUtil.invoke(supportedFileAttributeViewsMethod, fileSystem);
    
    return views.contains("posix");
  }
}