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

org.zanata.util.PathUtil Maven / Gradle / Ivy

package org.zanata.util;

import java.io.File;
import java.io.IOException;
import java.util.regex.Pattern;

import javax.annotation.Nullable;

import org.apache.commons.io.FilenameUtils;

/**
 * From
 * http://stackoverflow.com/questions/204784/how-to-construct-a-relative-path
 * -in-java-from-two-absolute-paths-or-urls/3054692#3054692
 *
 */

public class PathUtil {
    private static final String FILESEP = System.getProperty("file.separator");

    /**
     * Get the relative path from one file to another, specifying the directory
     * separator. If one of the provided resources does not exist, it is assumed
     * to be a file unless it ends with '/' or '\'.
     *
     * @param target
     *            targetPath is calculated to this file
     * @param base
     *            basePath is calculated from this file
     * @return
     * @throws PathResolutionException
     *             if no relative path exists
     */
    public static String getRelativePath(String targetPath, String basePath)
            throws PathResolutionException {
        return getRelativePath(targetPath, basePath, FILESEP);
    }

    /**
     * Get the relative path from one file to another, specifying the directory
     * separator. If one of the provided resources does not exist, it is assumed
     * to be a file unless it ends with '/' or '\'.
     *
     * @param target
     *            targetPath is calculated to this file
     * @param base
     *            basePath is calculated from this file
     * @param separator
     *            directory separator. The platform default is not assumed so
     *            that we can test Unix behaviour when running on Windows (for
     *            example)
     * @return
     * @throws PathResolutionException
     *             if no relative path exists
     */
    public static String getRelativePath(String targetPath, String basePath,
            String pathSeparator) throws PathResolutionException {

        // Normalize the paths
        String normalizedTargetPath =
                FilenameUtils.normalizeNoEndSeparator(targetPath);
        String normalizedBasePath =
                FilenameUtils.normalizeNoEndSeparator(basePath);

        // Undo the changes to the separators made by normalization
        if (pathSeparator.equals("/")) {
            normalizedTargetPath =
                    FilenameUtils.separatorsToUnix(normalizedTargetPath);
            normalizedBasePath =
                    FilenameUtils.separatorsToUnix(normalizedBasePath);

        } else if (pathSeparator.equals("\\")) {
            normalizedTargetPath =
                    FilenameUtils.separatorsToWindows(normalizedTargetPath);
            normalizedBasePath =
                    FilenameUtils.separatorsToWindows(normalizedBasePath);

        } else {
            throw new IllegalArgumentException("Unrecognised dir separator '"
                    + pathSeparator + "'");
        }

        String[] base = normalizedBasePath.split(Pattern.quote(pathSeparator));
        String[] target =
                normalizedTargetPath.split(Pattern.quote(pathSeparator));

        // First get all the common elements. Store them as a string,
        // and also count how many of them there are.
        StringBuilder common = new StringBuilder();

        int commonIndex = 0;
        while (commonIndex < target.length && commonIndex < base.length
                && target[commonIndex].equals(base[commonIndex])) {
            common.append(target[commonIndex] + pathSeparator);
            commonIndex++;
        }

        if (commonIndex == 0) {
            // No single common path element. This most
            // likely indicates differing drive letters, like C: and D:.
            // These paths cannot be relativized.
            throw new PathResolutionException(
                    "No common path element found for '" + normalizedTargetPath
                            + "' and '" + normalizedBasePath + "'");
        }

        // The number of directories we have to backtrack depends on whether the
        // base is a file or a dir
        // For example, the relative path from
        //
        // /foo/bar/baz/gg/ff to /foo/bar/baz
        //
        // ".." if ff is a file
        // "../.." if ff is a directory
        //
        // The following is a heuristic to figure out if the base refers to a
        // file
        // or dir. It's not perfect, because
        // the resource referred to by this path may not actually exist, but
        // it's
        // the best I can do
        boolean baseIsFile = true;

        File baseResource = new File(normalizedBasePath);

        if (baseResource.exists()) {
            baseIsFile = baseResource.isFile();

        } else if (basePath.endsWith(pathSeparator)) {
            baseIsFile = false;
        }

        StringBuilder relative = new StringBuilder();

        if (base.length != commonIndex) {
            int numDirsUp =
                    baseIsFile ? base.length - commonIndex - 1 : base.length
                            - commonIndex;

            for (int i = 0; i < numDirsUp; i++) {
                relative.append(".." + pathSeparator);
            }
        }
        relative.append(normalizedTargetPath.substring(common.length()));
        return relative.toString();
    }

    /**
     * Returns relative path from parentDir to f. NB: assumes that f is inside
     * parentDir.
     *
     * @param f
     *            a file inside parentDir
     * @param parentDir
     *
     * @return
     * @throws IOException
     * @throws PathResolutionException
     */
    public static String getSubPath(File f, File parentDir) throws IOException,
            PathResolutionException {
        // we could use canonical path here, but we don't want symlinks to be
        // resolved
        String dirPath = parentDir.getAbsolutePath();
        String filePath = f.getAbsolutePath();
        if (!filePath.startsWith(dirPath)) {
            throw new PathResolutionException("can't find relative path from "
                    + parentDir + " to " + f);
        }
        int skipSize = dirPath.length();
        if (!dirPath.endsWith(FILESEP))
            skipSize++;
        return filePath.substring(skipSize);
    }

    static class PathResolutionException extends RuntimeException {
        private static final long serialVersionUID = 1L;

        PathResolutionException(String msg) {
            super(msg);
        }
    }

    /**
     * Creates dir and its parents
     * @param dir directory to create, along with any required parent dirs
     * @throws IOException if any of the dirs do not exist and can't be created
     */
    public static void makeDirs(@Nullable File dir) throws IOException {
        if (dir != null && !dir.exists() && !dir.mkdirs()) {
            throw new IOException("unable to create directory (or parents): " + dir);
        }
    }

    public static boolean makeParents(File f) throws IOException {
        File parent = f.getCanonicalFile().getParentFile();
        if (parent == null || parent.exists()) {
            return false;
        }
        return parent.mkdirs();
    }

}




© 2015 - 2025 Weber Informatics LLC | Privacy Policy