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

org.qedeq.base.io.Path Maven / Gradle / Ivy

/* This file is part of the project "Hilbert II" - http://www.qedeq.org
 *
 * Copyright 2000-2013,  Michael Meyling .
 *
 * "Hilbert II" 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 2 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.
 */

package org.qedeq.base.io;

import java.util.ArrayList;
import java.util.List;

import org.apache.commons.lang.ArrayUtils;
import org.qedeq.base.utility.EqualsUtility;
import org.qedeq.base.utility.StringUtility;


/**
 * A file path that leads to a directory or file and is absolute or relative.
 * This abstraction of a file location was done to create relative file paths.
 * This class makes some assumptions about a file path:
 * "/" is the directory separator, "/" is also the root directory and ".." specifies
 * the parent directory. "." is the current directory.
 * A directory path must end with a "/" if a path ends not with a "/" it will be a file path
 * (but the empty path "" is a directory).
 *
 * @author  Michael Meyling
 */
public final class Path {

    /** Directories that build up the path. If this is an absolute path the first
     * name is empty. */
    private final String[] path;

    /** File name. */
    private final String name;

    /**
     * Create file with given path and name.
     *
     * @param   filePath    Path to file with "/" as directory name separator. Relative directories
     *                      are removed from the file path if possible.  Must not be
     *                      null. Might be "" this is called the empty path.
     */
    public Path(final String filePath) {
        final String[] p = StringUtility.split(filePath, "/");
        name = p[p.length - 1];
        final String[] p2 = new String[p.length - 1];
        System.arraycopy(p, 0, p2, 0, p2.length);
        path = removeRelativeDirs(p2);
    }

    /**
     * Create file with given path and name. Relative directories are removed from
     * the file path if possible.
     *
     * @param   dirPath     Directory path to file with "/" as directory name separator.
     *                      This value can end with a "/" but it must not be null.
     * @param   fileName    File name. It should not contain "/" but this is not checked.
     */
    public Path(final String dirPath, final String fileName) {
        this((dirPath.endsWith("/") ? StringUtility.split(dirPath.substring(0,
            dirPath.length() - 1), "/") : StringUtility.split(dirPath, "/")), fileName);
    }

    /**
     * Create file with given path and name. Relative directories are removed
     * from the file path if possible.
     *
     * @param   dirNames    Directory names. If this is an absolute path the first
     *                      name is empty. Must not be null.
     * @param   fileName    File name. It should not contain "/" but this is not checked.
     */
    public Path(final String[] dirNames, final String fileName) {
        path = removeRelativeDirs(dirNames);
        name = (fileName != null ? fileName : "");
    }

    /**
     * Create new file path relative to given one. If the original path or filePath
     * is a relative path it will return filePath.
     *
     * @param   filePath    Path to file relative to this.
     * @return  Relative file path (if possible).
     */
    public Path createRelative(final String filePath) {
        final Path to = new Path(filePath);
        if (isRelative()) {
            // if from is relative, then we don't need to think further
            return to;
        }
        if (to.isRelative()) {
            return to;
        }
        // both are absolute so we try to navigate within the file system
        // to get a relative path
        int max = 0;
        while (max < path.length && max < to.path.length) {
            if (!"..".equals(path[max]) && EqualsUtility.equals(path[max], to.path[max])) {
                max++;
            } else {
                break;
            }
        }
        final String[] r = new String[path.length - max + to.path.length - max];
        for (int i = max; i < path.length; i++) {
            r[i - max] = "..";
        }
        for (int i = max; i < to.path.length; i++) {
            r[i - max + path.length - max] = to.path[i];
        }
        return new Path(r, to.name);
    }

    /**
     * Describes this path a directory?
     *
     * @return  Is directory?
     */
    public boolean isDirectory() {
        return name.length() == 0;
    }

    /**
     * Describes this path a file (and not a directory)?
     *
     * @return  Is this a path to a file?
     */
    public boolean isFile() {
        return !isDirectory();
    }

    /**
     * Is this an absolute path? If first path directory name is empty or ends with ":"
     * (MS windows tribute) this is the case.
     *
     * @return  Is absolute path?
     */
    public boolean isAbsolute() {
        return path.length > 0 && (path[0].length() == 0 || path[0].endsWith(":"));
    }

    /**
     * Is this a relative path?
     *
     * @return  Is this a relative path?
     */
    public boolean isRelative() {
        return !isAbsolute();
    }

    /**
     * Get filename. Is "" if this is a pure directory path.
     *
     * @return  File name. Might be "" but not null.
     */
    public String getFileName() {
        return name;
    }

    /**
     * Get directory of this path. This might be a relative or absolute
     * path and ends with "/". (Only the empty path "" has no ending "/".)
     *
     * @return  Directory this path points to. Will end with "/" if this is not
     *          an empty path.
     */
    public String getDirectory() {
        StringBuffer result = new StringBuffer(256);
        for (int i = 0; i < path.length; i++) {
            result.append(path[i]).append("/");
        }
        return result.toString();
    }

    /**
     * Remove ".." and "." directories out of path if possible.
     *
     * @param   dirNames    Directories that build up the path.
     * @return  Directories that build up the same path.
     */
    private String[] removeRelativeDirs(final String[] dirNames) {
        List d = new ArrayList();
        for (int i = 0; i < dirNames.length; i++) {
            d.add(dirNames[i]);
        }
        for (int i = 0; i < d.size(); ) {
            if (i > 0 && "..".equals(d.get(i)) && !"".equals(d.get(i - 1))
                    && !"..".equals(d.get(i - 1))) {
                d.remove(i - 1);
                d.remove(i - 1);
                i--;
            } else if (".".equals(d.get(i))) {
                d.remove(i);
            } else {
                i++;
            }
        }
        return (String[]) d.toArray(ArrayUtils.EMPTY_STRING_ARRAY);
    }

    public String toString() {
        StringBuffer result = new StringBuffer(256);
        for (int i = 0; i < path.length; i++) {
            result.append(path[i]).append("/");
        }
        result.append(name);
        return result.toString();
    }

    public boolean equals(final Object obj) {
        if (!(obj instanceof Path)) {
            return false;
        }
        final Path other = (Path) obj;
        return EqualsUtility.equals(path, other.path) && name.equals(other.name);
    }

    public int hashCode() {
        return toString().hashCode();
    }

}





© 2015 - 2024 Weber Informatics LLC | Privacy Policy