001/*
002 * Copyright (C) Photon Vision.
003 *
004 * This program is free software: you can redistribute it and/or modify
005 * it under the terms of the GNU General Public License as published by
006 * the Free Software Foundation, either version 3 of the License, or
007 * (at your option) any later version.
008 *
009 * This program is distributed in the hope that it will be useful,
010 * but WITHOUT ANY WARRANTY; without even the implied warranty of
011 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
012 * GNU General Public License for more details.
013 *
014 * You should have received a copy of the GNU General Public License
015 * along with this program.  If not, see <https://www.gnu.org/licenses/>.
016 */
017
018package org.photonvision.common.util.file;
019
020import java.io.File;
021import java.io.FileNotFoundException;
022import java.io.IOException;
023import java.nio.file.Files;
024import java.nio.file.NoSuchFileException;
025import java.nio.file.Path;
026import java.nio.file.attribute.PosixFileAttributes;
027import java.nio.file.attribute.PosixFilePermission;
028import java.util.Arrays;
029import java.util.Comparator;
030import java.util.HashSet;
031import java.util.Set;
032import org.photonvision.common.hardware.Platform;
033import org.photonvision.common.logging.LogGroup;
034import org.photonvision.common.logging.Logger;
035
036public class FileUtils {
037    private FileUtils() {}
038
039    private static final Logger logger = new Logger(FileUtils.class, LogGroup.General);
040    private static final Set<PosixFilePermission> allReadWriteExecutePerms =
041            new HashSet<>(Arrays.asList(PosixFilePermission.values()));
042
043    public static boolean deleteDirectory(Path path) {
044        try {
045            var files = Files.walk(path);
046
047            // delete directory including files and sub-folders
048            files
049                    .sorted(Comparator.reverseOrder())
050                    .map(Path::toFile)
051                    // .filter(File::isFile) // we want to delete directories and sub-dirs, too
052                    .forEach((var file) -> deleteFile(file.toPath()));
053
054            // close the stream
055            files.close();
056
057            return true;
058        } catch (IOException e) {
059            logger.error("Exception deleting files in " + path + "!", e);
060            return false;
061        }
062    }
063
064    /**
065     * Delete the file at the path.
066     *
067     * @param path file path to delete.
068     * @return whether the operation was successful.
069     */
070    public static boolean deleteFile(Path path) {
071        try {
072            Files.delete(path);
073            return true;
074        } catch (FileNotFoundException | NoSuchFileException fe) {
075            logger.warn("Tried to delete file \"" + path + "\" but it did not exist");
076            return false;
077        } catch (IOException e) {
078            logger.error("Exception deleting file \"" + path + "\"!", e);
079            return false;
080        }
081    }
082
083    /**
084     * Copy a file from a source to a new destination.
085     *
086     * @param src the file path to copy.
087     * @param dst the file path to replace.
088     * @return whether the operation was successful.
089     */
090    public static boolean copyFile(Path src, Path dst) {
091        try {
092            Files.copy(src, dst);
093            return true;
094        } catch (IOException e) {
095            logger.error("Exception copying file " + src + " to " + dst + "!", e);
096            return false;
097        }
098    }
099
100    /**
101     * Replace the destination file with a new source.
102     *
103     * @param src the file path to replace with.
104     * @param dst the file path to replace.
105     * @return whether the operation was successful.
106     */
107    public static boolean replaceFile(Path src, Path dst) {
108        boolean fileDeleted = deleteFile(dst);
109        boolean fileCopied = copyFile(src, dst);
110        return fileDeleted && fileCopied;
111    }
112
113    public static void setFilePerms(Path path) throws IOException {
114        if (Platform.isLinux()) {
115            File thisFile = path.toFile();
116            Set<PosixFilePermission> perms =
117                    Files.readAttributes(path, PosixFileAttributes.class).permissions();
118            if (!perms.equals(allReadWriteExecutePerms)) {
119                logger.info("Setting perms on" + path);
120                Files.setPosixFilePermissions(path, perms);
121                var theseFiles = thisFile.listFiles();
122                if (thisFile.isDirectory() && theseFiles != null) {
123                    for (File subfile : theseFiles) {
124                        setFilePerms(subfile.toPath());
125                    }
126                }
127            }
128        }
129    }
130
131    public static void setAllPerms(Path path) {
132        if (Platform.isLinux()) {
133            String command = String.format("chmod 777 -R %s", path.toString());
134            try {
135                Process p = Runtime.getRuntime().exec(command);
136                p.waitFor();
137
138            } catch (Exception e) {
139                logger.error("Setting perms failed!", e);
140            }
141        } else {
142            logger.info("Cannot set directory permissions on Windows!");
143        }
144    }
145}