All Downloads are FREE. Search and download functionalities are using the official Maven repository.

uk.ac.liv.pgb.analytica.lib.wrappedr.FileSystem Maven / Gradle / Ivy

Go to download

Library allowing generation of plots and data from proteomics and metabolomics mass spectrometry data through the use of R and java methods.

The newest version!
package uk.ac.liv.pgb.analytica.lib.wrappedr;

import java.io.File;
import java.io.FileNotFoundException;
import java.io.IOException;
import java.io.OutputStream;
import java.io.PrintStream;
import java.lang.reflect.Field;
import java.util.Arrays;
import java.util.Collections;
import java.util.HashMap;
import java.util.HashSet;
import java.util.LinkedList;
import java.util.List;
import java.util.Map;
import java.util.Map.Entry;
import java.util.Optional;
import java.util.Set;
import java.util.logging.Level;
import java.util.logging.Logger;
import java.util.prefs.Preferences;
import java.util.stream.StreamSupport;

/**
 * Utility class to help to find files/executables and to manipulate
 * environments.
 *
 * @author sperkins
 */
public final class FileSystem {

    /**
     * Map of found commands/executables from their names.
     */
    private static final Map PREVIOUS_FIND_COMMAND_RESULTS = new HashMap<>();

    /**
     * Set of directories that commands/executables were found in.
     */
    private static final Set PREVIOUS_FIND_COMMAND_DIRECTORIES = new HashSet<>();

    /**
     * Map of found files from their names.
     */
    private static final Map PREVIOUS_FIND_FILE_RESULTS = new HashMap<>();

    /**
     * Type of the operating system.
     */
    private static SystemType systemType = null;

    /**
     * Whether the JVM is running in administrator mode or not.
     */
    private static final boolean ADMIN_MODE = isAdmin();

    /**
     * Private constructor to prevent instantiation.
     */
    private FileSystem() {
    }

    /**
     * Recursively deletes the path represented by the given file..
     *
     * @param path The path represented by a file.
     * @return True if the path is successfully deleted.
     * @throws FileNotFoundException Thrown if the path does not exist.
     */
    public static boolean deleteRecursive(final File path) throws FileNotFoundException {
        if (!path.exists()) {
            throw new FileNotFoundException(path.getAbsolutePath());
        }

        File[] children = path.listFiles();

        if (children == null) {
            return path.delete();
        }

        return Arrays.stream(children).parallel().map(child -> {
            if (child.isFile()) {
                return child.delete();
            } else if (child.isDirectory()) {
                try {
                    return deleteRecursive(child);
                } catch (FileNotFoundException ex) {
                    return false;
                }
            }

            return true;
        }).allMatch(deleted -> deleted) ? path.delete() : false;
    }

    /**
     * Finds a executable/command from its name.
     *
     * @param command Executable/command name.
     * @return The command/executable represented by a file.
     */
    public static File findExecutionCommand(final String command) {
        return findExecutionCommand(command, Collections.emptyList());
    }

    /**
     * Finds a executable/command from its name, given some location hints.
     *
     * @param command Executable/command name.
     * @param locationHints Hints to the location of the command/executable.
     * @return The command/executable represented by a file.
     */
    public static File findExecutionCommand(final String command, final Iterable locationHints) {
        // First let's see if we have seen this command before in this session.
        if (PREVIOUS_FIND_COMMAND_RESULTS.containsKey(command)) {
            return PREVIOUS_FIND_COMMAND_RESULTS.get(command);
        }

        // We haven't seen it before, so let's see if the command is on the system path.
        // We use the 'where' command for windows, and the 'which' command for linux.
        SystemType type = getSystemType();
        CommandExecutor exec = null;
        switch (type) {
            case WIN32:
            case WIN64:
                exec = new CommandExecutor(findFile("where.exe", Arrays.asList(File.listRoots())));
                break;
            case LINUX32:
            case LINUX64:
                exec = new CommandExecutor(findFile("which", Arrays.asList(File.listRoots())));
            default:
                break;
        }

        if (exec != null) {
            try {
                exec.execute(Collections.singletonList(command));

            } catch (InterruptedException ex) {

            }

            List output = exec.getOutput();

            if (output != null) {
                for (String outputLine : output) {
                    if (!outputLine.startsWith("INFO") && !outputLine.startsWith("JINKIES")) {
                        File file = new File(outputLine.trim());
                        PREVIOUS_FIND_COMMAND_RESULTS.put(command, file);
                        PREVIOUS_FIND_COMMAND_DIRECTORIES.add(file.getParentFile());
                        try {
                            return file.getCanonicalFile();
                        } catch (IOException ex) {

                        }
                    }
                }
            }
        }

        // Command wasn't in the path, so let's check the location hints.
        File foundFile = findFile(command, locationHints);
        if (foundFile != null) {
            PREVIOUS_FIND_COMMAND_RESULTS.put(command, foundFile);
            PREVIOUS_FIND_COMMAND_DIRECTORIES.add(foundFile.getParentFile());
            return foundFile;
        }

        // Command wasn't in the hinted locations, so we have to be more inventive.
        foundFile = findFile(command, PREVIOUS_FIND_COMMAND_DIRECTORIES);
        if (foundFile != null) {
            PREVIOUS_FIND_COMMAND_RESULTS.put(command, foundFile);
            PREVIOUS_FIND_COMMAND_DIRECTORIES.add(foundFile.getParentFile());
            return foundFile;
        }

        // Command wasn't in the most recent directories that other commands where found in.
        // Let's look through the file system. This is very expensive, should not normally be triggered.
        System.out.println("Warning: searching file systems for '" + command + "'");
        foundFile = findFile(command, Arrays.asList(File.listRoots()));
        if (foundFile != null) {
            PREVIOUS_FIND_COMMAND_RESULTS.put(command, foundFile);
            PREVIOUS_FIND_COMMAND_DIRECTORIES.add(foundFile.getParentFile());
            return foundFile;
        }

        // If we ever get to this point the command was nowhere to be found, so return null;
        return null;
    }

    /**
     * Finds a file from its name in the given directories.
     *
     * @param fileName The file name.
     * @param directories Directories in which to look for the file.
     * @return The found file.
     */
    private static File findFile(final String fileName, final Iterable directories) {
        return findFile(fileName, directories, false);
    }

    /**
     * Finds a file from its name in the given directories..
     *
     * @param fileName The file name.
     * @param directories Directories in which to look for the file.
     * @param skipSystemLocationCheck Whether to skip looking in system
     * locations.
     * @return The found file.
     */
    private static File findFile(final String fileName, final Iterable directories, final boolean skipSystemLocationCheck) {

        Optional foundFileOptional = StreamSupport.stream(directories.spliterator(), true).filter(p -> p.isDirectory()).map(p -> {
            return findFile(fileName, p, skipSystemLocationCheck, false);
        }).filter(p -> p != null).findAny();

        return foundFileOptional.isPresent() ? foundFileOptional.get() : null;
    }

    /**
     * Finds a file from its name in the given directories..
     *
     * @param fileName The file name.
     * @param rootDirectory The directory in which to look for the file.
     * @return The found file.
     */
    public static File findFile(final String fileName, final File rootDirectory) {
        return findFile(fileName, rootDirectory, false, false);
    }

    /**
     * Finds a file from its name in the given directories..
     *
     * @param fileName The file name.
     * @param rootDirectory The directory in which to look for the file.
     * @param skipSystemLocationCheck Whether to skip looking in system
     * locations.
     * @param isDirectory Whether the file we are looking for is in fact a
     * directory.
     * @return The found file.
     */
    public static File findFile(final String fileName, final File rootDirectory, final boolean skipSystemLocationCheck, final boolean isDirectory) {
        if (PREVIOUS_FIND_FILE_RESULTS.containsKey(fileName)) {
            return PREVIOUS_FIND_FILE_RESULTS.get(fileName);
        }

        if (!skipSystemLocationCheck) {
            File systemLocationsFile = findFile(fileName, Arrays.asList(new File[]{new File("c:\\Windows\\System32"), new File("//usr/bin")}), true);
            if (systemLocationsFile != null) {
                return systemLocationsFile;
            }
        }

        if (rootDirectory == null || !rootDirectory.exists() || !rootDirectory.isDirectory()) {
            return null;
        }

        File[] children = rootDirectory.listFiles();

        if (children == null) {
            return null;
        }

        // Speed up finding object if it in the top level directory.
        Optional topDirectoryOptional = Arrays.stream(children).filter(child -> {
            if ((child.isDirectory() && isDirectory) || (child.isFile() && !isDirectory)) {
                if (getSystemType().equals(SystemType.WIN32) || getSystemType().equals(SystemType.WIN64)) {
                    if (child.getName().toUpperCase().equals(fileName.toUpperCase())) {
                        PREVIOUS_FIND_FILE_RESULTS.put(fileName, child);
                        return true;
                    }
                } else if (child.getName().equals(fileName)) {
                    PREVIOUS_FIND_FILE_RESULTS.put(fileName, child);
                    return true;
                }
            }

            return false;
        }).findAny();

        if (topDirectoryOptional.isPresent()) {
            return topDirectoryOptional.get();
        }

        // Don't need to scan current directory level as we have just done it above.
        Optional subDirectoryOptional = Arrays.stream(children).parallel().filter(p -> p.isDirectory()).map(child -> {
            File foundFile = findFile(fileName, child, true, isDirectory);
            return foundFile;
        }).filter(p -> p != null).findAny();

        return subDirectoryOptional.isPresent() ? subDirectoryOptional.get() : null;
    }

    /**
     * Add a folder to the permanent path variable.
     *
     * @param folder Folder to add to the path permanently.
     * @return True if there was no error setting the path.
     */
    public static boolean addFolderToPathPermanent(final File folder) {
        throw new UnsupportedOperationException();
    }

    /**
     * Add a folder to the path variable for the session, in a few places.
     *
     * @param folder Folder to add to the path for the session.
     * @return True if there was no error setting the path.
     */
    public static boolean addFolderToPathForJavaSession(final File folder) {
        String existingPath = System.getProperty("java.library.path");
        if (existingPath.endsWith(".")) {
            existingPath = existingPath.substring(0, existingPath.length() - 1);
        }

        System.setProperty("java.library.path", existingPath + folder.getAbsolutePath() + File.separator + File.pathSeparator);
        try {
            Field fieldSysPath = ClassLoader.class.getDeclaredField("sys_paths");
            fieldSysPath.setAccessible(true);
            fieldSysPath.set(null, null);
        } catch (NoSuchFieldException | SecurityException | IllegalArgumentException | IllegalAccessException ex) {
            return false;
        }

        try {
            Class theClass = Class.forName("java.lang.ProcessEnvironment");
            Field field1 = theClass.getDeclaredField("theEnvironment");
            field1.setAccessible(true);
            Map theEnvironment = (Map) field1.get(null);

            Field field2 = theClass.getDeclaredField("theCaseInsensitiveEnvironment");
            field2.setAccessible(true);
            Map caseInsensitiveEnvironment = (Map) field1.get(null);

            Optional> paths1 = theEnvironment.entrySet().stream().filter(p -> p.getKey().equalsIgnoreCase("PATH")).findFirst();
            if (paths1.isPresent()) {
                paths1.get().setValue(paths1.get().getValue() + ";" + folder.getAbsolutePath());
            }

            Optional> paths2 = caseInsensitiveEnvironment.entrySet().stream().filter(p -> p.getKey().equalsIgnoreCase("PATH")).findFirst();
            if (paths2.isPresent()) {
                paths2.get().setValue(paths2.get().getValue() + ";" + folder.getAbsolutePath());
            }
        } catch (ClassNotFoundException | NoSuchFieldException | SecurityException | IllegalArgumentException | IllegalAccessException ex) {
            return false;
        }

        return true;
    }

    /**
     * Set an environment variable permanently. If running in administrator
     * mode, a system-level variable will be set, otherwise a a user-level
     * variable.
     *
     * @param name The environment variable name.
     * @param value The environment variable value.
     * @return True if there was no error setting the environment variable.
     */
    public static boolean setEnvironmentVariablePermanent(final String name, final String value) {        
        if (systemType == SystemType.WIN32 || systemType == SystemType.WIN64) {
            File setxCommand = findExecutionCommand("setx");
            if (setxCommand == null) {
                return false;
            }

            CommandExecutor executor = new CommandExecutor(setxCommand);
            List arguments = new LinkedList<>();
            arguments.add(name);
            arguments.add(value);
            if (ADMIN_MODE) {
                arguments.add("/M");
            }

            try {
                executor.execute(arguments);
            } catch (InterruptedException ex) {
                return false;
            }
        } else if (systemType == SystemType.LINUX32 || systemType == SystemType.LINUX64) {
            if (ADMIN_MODE) {
                File profileD = new File("/etc/profile.d");
                if (profileD.exists() && profileD.isDirectory()) {
                    // Let's add a script to the profile.d directory to make the environment variable change permanent.
                    throw new UnsupportedOperationException("Not yet implemented.");
                } else {
                    File profile = new File("/etc/profile");
                    if (profile.exists() && profile.isFile()) {
                        // Let's edit the profile file directly to make the environment variable change permanent.
                        throw new UnsupportedOperationException("Not yet implemented.");
                    }
                }
            }           
        } else {
            // System type was not one of the four we support, so we cannot set the environment variable.
            return false;
        }

        return true;
    }

    /**
     * Set an environment variable for the session.
     *
     * @param name The environment variable name.
     * @param value The environment variable value.
     * @return True if there was no error setting the environment variable.
     */
    public static boolean setEnvironmentVariableForJavaSession(final String name, final String value) {
        try {
            Class theClass = Class.forName("java.lang.ProcessEnvironment");
            Field field1 = theClass.getDeclaredField("theEnvironment");
            field1.setAccessible(true);
            Map theEnvironment = (Map) field1.get(null);
            theEnvironment.put(name, value);

            Field field2 = theClass.getDeclaredField("theCaseInsensitiveEnvironment");
            field2.setAccessible(true);
            Map caseInsensitiveEnvironment = (Map) field1.get(null);
            caseInsensitiveEnvironment.put(name, value);

        } catch (ClassNotFoundException | NoSuchFieldException | SecurityException | IllegalArgumentException | IllegalAccessException ex) {
            return false;
        }

        return true;
    }

    /**
     * Checks whether the JVM was started with administrator privileges.
     *
     * @return True if the JVM was started with administrator privileges.
     */
    private static boolean isAdmin() {
        Preferences prefs = Preferences.systemRoot();
        PrintStream systemErr = System.err;
        synchronized (systemErr) {
            System.setErr(new PrintStream(new OutputStream() {
                @Override
                public void write(int i) throws IOException {
                }
            }));

            try {
                // SecurityException on windows if not admin.
                prefs.put("foo", "bar");
                prefs.remove("foo");
                // BackingStoreException in linux if not admin.
                prefs.flush();
                return true;
            } catch (Exception ex) {
                return false;
            } finally {
                System.setErr(systemErr);
            }
        }
    }

    /**
     * Gets the system type.
     *
     * @return The system type.
     */
    public static SystemType getSystemType() {
        if (systemType != null) {
            return systemType;
        }

        String operatingSystemType = System.getProperty("os.name");
        if (operatingSystemType.startsWith("Windows")) {
            if (System.getenv("ProgramFiles(x86)") != null) {
                systemType = SystemType.WIN64;
            } else {
                systemType = SystemType.WIN32;
            }
        } else {
            CommandExecutor exec = new CommandExecutor(findFile("uname.sh", Arrays.asList(File.listRoots())));
            try {
                exec.execute(Collections.singletonList("-a"));
            } catch (InterruptedException ex) {

            }

            systemType = SystemType.LINUX32;
            List output = exec.getOutput();
            for (String outputLine : output) {
                if (outputLine.contains("x86_64")
                        || outputLine.contains("ia64")) {
                    systemType = SystemType.LINUX64;
                    break;
                }
            }
        }

        return systemType;
    }

    /**
     * The system type.
     */
    public enum SystemType {
        /**
         * 32-bit windows system.
         */
        WIN32,
        /**
         * 64-bit windows system.
         */
        WIN64,
        /**
         * 32-bit linux system.
         */
        LINUX32,
        /**
         * 64-bit linux system.
         */
        LINUX64
    }
}




© 2015 - 2025 Weber Informatics LLC | Privacy Policy