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

com.palantir.giraffe.file.UniformPath Maven / Gradle / Ivy

There is a newer version: 0.10.1
Show newest version
/**
 * Copyright 2015 Palantir Technologies, Inc.
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *     http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */
package com.palantir.giraffe.file;

import static com.google.common.base.Preconditions.checkArgument;
import static com.google.common.base.Preconditions.checkNotNull;

import java.nio.file.FileSystem;
import java.nio.file.FileSystems;
import java.nio.file.Path;
import java.util.Iterator;
import java.util.NoSuchElementException;

import com.google.common.base.Function;
import com.google.common.base.Joiner;
import com.google.common.collect.ImmutableList;
import com.google.common.collect.Iterables;
import com.google.common.collect.Lists;
import com.google.common.collect.UnmodifiableIterator;
import com.palantir.giraffe.file.base.ImmutableListPathCore;
import com.palantir.giraffe.file.base.ImmutableListPathCore.Parser.NamedRootFormat;

// @formatter:off
/**
 * A {@link Path}-like object that defines a file system independent path syntax
 * and is not associated with any {@code FileSystem} instance.
 * 

* Uniform paths use the following UNIX-like syntax: *

* {@code [[//root]/]path/to/file/or/directory} *
where {@code []} indicates an optional component and {@code /} * serves as the path separator. All names are case-sensitive. *

* If the root component is present, the path is absolute. For single-root file * systems, the root prefix {@code //} and the root name are omitted and * the path starts with a single forward slash. For multi-root file system, the * path starts with the root prefix followed by the name of the root and a * single forward slash. Root names that contain the path separator ({@code /}) * are not allowed. *

* {@code UniformPath}s can be converted to standard {@code Path}s using the * {@link #toPath(FileSystem) toPath} method. * * @author bkeyes */ // @formatter:on public final class UniformPath implements Iterable, Comparable { private static final String SEPARATOR = "/"; private static final String ROOT_PREFIX = "//"; private static final ImmutableListPathCore.Parser PARSER = ImmutableListPathCore.namedRootParser( SEPARATOR, NamedRootFormat.OPTIONAL_PREFIX, ROOT_PREFIX); /** * Converts a path string, or a sequence of strings that form a path string * when joined, to a {@code UniformPath}. * * @param first the path string or initial part of the path string * @param more additional strings to join to form the path string */ public static UniformPath get(String first, String... more) { return get(Lists.asList(first, more)); } /** * Converts a sequence of strings that form a path string when joined to a * {@code UniformPath}. * * @param components the components to join to form the path string */ public static UniformPath get(Iterable components) { return fromString(Joiner.on(SEPARATOR).join(components)); } /** * Converts the specified {@link Path} into a {@code UniformPath}. *

* For any path {@code p}, the following relationship will always hold: * *

     * {@code UniformPath.fromPath(p).toPath(p.getFileSystem()).equals(p)}
     * 
* * @param path the path to convert * * @return a {@code UniformPath} that is equivalent to the given path * * @throws IllegalArgumentException if any path segment contains the uniform * path separator */ public static UniformPath fromPath(Path path) { checkNotNull(path, "path must be non-null"); Iterable segments = Iterables.transform(path, new Function() { @Override public String apply(Path input) { String seg = input.toString(); checkArgument(!seg.contains(SEPARATOR), "segment '%s' contains separator", seg); return seg; } }); return new UniformPath(new ImmutableListPathCore(getRootString(path), segments)); } private static String getRootString(Path p) { Path root = p.getRoot(); if (root == null) { return null; } else { String cleanRoot = cleanRoot(root.toString(), p.getFileSystem().getSeparator()); return cleanRoot.isEmpty() ? "" : ROOT_PREFIX + cleanRoot; } } private static String cleanRoot(String root, String fsSeparator) { String cleaned = root; if (root.endsWith(fsSeparator)) { cleaned = root.substring(0, root.length() - fsSeparator.length()); } checkArgument(!cleaned.contains(SEPARATOR), "root '%s' contains separator", root); return cleaned; } private static UniformPath fromString(String path) { return new UniformPath(PARSER.parse(path)); } private final ImmutableListPathCore core; private UniformPath(ImmutableListPathCore core) { this.core = core; } public boolean isAbsolute() { return core.isAbsolute(); } public UniformPath getRoot() { ImmutableListPathCore root = core.getRoot(); if (root == null) { return null; } else { return new UniformPath(root); } } public UniformPath getFileName() { ImmutableListPathCore name = core.getFileName(); if (name == null) { return null; } else { return new UniformPath(name); } } public UniformPath getParent() { ImmutableListPathCore parent = core.getParent(); if (parent == null) { return null; } else { return new UniformPath(parent); } } public int getNameCount() { return core.getNameCount(); } public UniformPath getName(int index) { return new UniformPath(core.getName(index)); } public UniformPath subpath(int beginIndex, int endIndex) { return new UniformPath(core.subpath(beginIndex, endIndex)); } public boolean startsWith(UniformPath other) { return core.startsWith(other.core); } public boolean startsWith(String other) { return startsWith(fromString(other)); } public boolean endsWith(UniformPath other) { return core.endsWith(other.core); } public boolean endsWith(String other) { return endsWith(fromString(other)); } public UniformPath normalize() { return new UniformPath(core.normalize()); } public UniformPath resolve(UniformPath other) { return new UniformPath(core.resolve(other.core)); } public UniformPath resolve(String other) { return resolve(fromString(other)); } public UniformPath resolveSibling(UniformPath other) { return new UniformPath(core.resolveSibling(other.core)); } public UniformPath resolveSibling(String other) { return resolveSibling(fromString(other)); } public UniformPath relativize(UniformPath other) { return new UniformPath(core.relativize(other.core)); } @Override public Iterator iterator() { return new UnmodifiableIterator() { private int i = 0; @Override public boolean hasNext() { return i < getNameCount(); } @Override public UniformPath next() { if (i < getNameCount()) { UniformPath result = getName(i); i++; return result; } else { throw new NoSuchElementException(); } } }; } /** * Converts this uniform path to a real {@link Path} on the default file * system. * * @see #toPath(FileSystem) */ public Path toPath() { return toPath(FileSystems.getDefault()); } /** * Converts this uniform path to a real {@link Path} on the given file * system. *

* If this path is a relative path, each segment is passed as a separate * argument to the file system's * {@link FileSystem#getPath(String, String...) getPath} method. If this * path is an absolute path, a relative path is constructed as described * then resolved against a root path obtained using the following method: *

    *
  1. Get all root directories on the file system
  2. *
  3. If this path does not have a named root and there is one * root, use that root
  4. *
  5. If there are multiple roots or this path has a named root, * compare this path's root to the string version of each file system root. * If this path's root equals a file system root, ignoring any trailing path * separator, use that root
  6. *
  7. If there are no matches, throw an {@code IllegalArgumentException}
  8. *
* * @param fs the {@link FileSystem} on which to create a path * * @throws IllegalArgumentException if this is an absolute path and the * given file system does not have a matching root */ public Path toPath(FileSystem fs) { checkNotNull(fs, "file system must be non-null"); if (isAbsolute()) { return findRoot(fs, getRootName()).resolve(toRelativePath(fs)); } else { return toRelativePath(fs); } } private Path findRoot(FileSystem fs, String target) { Iterable rootDirs = fs.getRootDirectories(); if (isSingleRoot() && Iterables.size(rootDirs) == 1) { return Iterables.getOnlyElement(rootDirs); } else { for (Path dir : rootDirs) { String root = dir.toString(); if (root.equals(target) || root.equals(target + fs.getSeparator())) { return dir; } } } throw new IllegalArgumentException("no root directory matching '" + target + "'"); } private Path toRelativePath(FileSystem fs) { ImmutableList segments = core.getPathSegments(); if (segments.isEmpty()) { return fs.getPath(""); } else { String first = segments.get(0); String[] more = segments.subList(1, segments.size()).toArray(new String[0]); return fs.getPath(first, more); } } private boolean isSingleRoot() { return isAbsolute() && core.getRootString().isEmpty(); } private String getRootName() { String rootString = core.getRootString(); if (rootString.startsWith(ROOT_PREFIX)) { return rootString.substring(ROOT_PREFIX.length()); } else { return rootString; } } @Override public int compareTo(UniformPath other) { return core.compareTo(other.core); } @Override public boolean equals(Object obj) { if (obj == this) { return true; } else if (!(obj instanceof UniformPath)) { return false; } else { return core.equals(((UniformPath) obj).core); } } @Override public int hashCode() { return core.hashCode(); } @Override public String toString() { return core.toPathString(SEPARATOR); } }




© 2015 - 2025 Weber Informatics LLC | Privacy Policy