/* * Path utility routines * * Copyright (C) 2013, 2014, 2016 Per Lundqvist * * This program is free software: you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation, either version 3 of the License, or * (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program. If not, see <http://www.gnu.org/licenses/>. */ package com.github.perlundq.yajsync.internal.util; import java.io.IOException; import java.net.URI; import java.net.URISyntaxException; import java.nio.file.FileSystem; import java.nio.file.FileSystemNotFoundException; import java.nio.file.FileSystems; import java.nio.file.InvalidPathException; import java.nio.file.Path; import java.util.Collections; import java.util.LinkedList; import java.util.List; import java.util.Map; import com.github.perlundq.yajsync.internal.text.Text; public final class PathOps { private PathOps() {} public static boolean isPathPreservable(Path path) { assert path != null; if (Environment.IS_RUNNING_WINDOWS) { return isWindowsPathPreserved(path); } return true; } private static boolean isWindowsPathPreserved(Path path) { assert path != null; for (Path p : path) { String name = p.toString(); if (name.endsWith(Text.DOT) && (!name.equals(Text.DOT) && !name.equals(Text.DOT_DOT))) { return false; } } return true; } public static boolean isDirectoryStructurePreservable(String separator, String unixPathName) { assert unixPathName != null; if (separator.equals(Text.SLASH)) { return true; } return !unixPathName.contains(separator); } // / - / = / // a - a = null // /a - /a = / // /a/b/c/d - c/d = /a/b // a/b/c/d - c/d = a/b // ././. - . = ./. public static Path subtractPathOrNull(Path parent, Path sub) { if (!parent.endsWith(sub)) { throw new IllegalArgumentException(String.format( "%s is not a parent path of %s", parent, sub)); } if (parent.getNameCount() == sub.getNameCount()) { return parent.getRoot(); // NOTE: return null if parent has no root } Path res = parent.subpath(0, parent.getNameCount() - sub.getNameCount()); if (parent.isAbsolute()) { return parent.getRoot().resolve(res); } else { return res; } } public static boolean contains(Path path, Path searchPath) { for (Path subPath : path) { if (subPath.equals(searchPath)) { return true; } } return false; } public static Path normalizeStrict(Path path) { if (Environment.IS_RUNNING_WINDOWS) { return normalizePathWin(path); } return normalizePathDefault(path); } private static Path joinPaths(Path path, List<Path> paths) { Path empty = path.getFileSystem().getPath(Text.EMPTY); Path result = path.isAbsolute() ? path.getRoot() : empty; for (Path p : paths) { result = result.resolve(p); } return result; } /** * @throws InvalidPathException if trying to resolve a relative path * prefixed with a .. directory */ private static Path normalizePathWin(Path path) { LinkedList<Path> paths = new LinkedList<>(); Path dotDotDir = path.getFileSystem().getPath(Text.DOT_DOT); for (Path p : path) { if (p.equals(dotDotDir)) { if (paths.isEmpty()) { throw new InvalidPathException(path.toString(), "cannot resolve .."); } paths.removeLast(); } else { String normalized = Text.deleteTrailingDots(p.toString()).toLowerCase(); if (!normalized.isEmpty()) { paths.add(path.getFileSystem().getPath(normalized)); } } } return joinPaths(path, paths); } // we can't use Path.normalize because it resolves a/../.. -> .. for example private static Path normalizePathDefault(Path path) { LinkedList<Path> paths = new LinkedList<>(); Path dotDir = path.getFileSystem().getPath(Text.DOT); Path dotDotDir = path.getFileSystem().getPath(Text.DOT_DOT); for (Path p : path) { if (p.equals(dotDotDir)) { if (paths.isEmpty()) { throw new InvalidPathException(path.toString(), "cannot resolve .."); } paths.removeLast(); } else if (!p.equals(dotDir)) { paths.add(p); } } return joinPaths(path, paths); } /** * Preserves trailing slash information (FileSystem.getPath won't) and * normalize empty paths to "." * * @throws InvalidPathException */ public static Path get(FileSystem fs, String name) { Path normalized = fs.getPath(name).normalize(); if (normalized.toString().equals(Text.EMPTY)) { return fs.getPath(Text.DOT); } else if (name.endsWith(Text.SLASH) || name.endsWith(Text.SLASH + Text.DOT)) { return normalized.resolve(Text.DOT); } else { return normalized; } } public static FileSystem fileSystemOf(String fsPathName) throws IOException, URISyntaxException { URI uri = new URI(fsPathName); try { return FileSystems.getFileSystem(uri); } catch (FileSystemNotFoundException e) { Map<String, Object> empty = Collections.emptyMap(); return FileSystems.newFileSystem(uri, empty); } } }