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

com.google.cloud.storage.contrib.nio.UnixPath Maven / Gradle / Ivy

There is a newer version: 0.127.26
Show newest version
/*
 * Copyright 2016 Google LLC
 *
 * 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.google.cloud.storage.contrib.nio;

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

import com.google.common.base.Joiner;
import com.google.common.base.Splitter;
import com.google.common.collect.Iterators;
import com.google.common.collect.Lists;
import com.google.common.collect.Ordering;
import com.google.common.collect.PeekingIterator;
import java.util.ArrayList;
import java.util.Collections;
import java.util.Iterator;
import java.util.List;
import javax.annotation.Nullable;
import javax.annotation.concurrent.Immutable;

/**
 * Unix file system path.
 *
 * 

This class is helpful for writing {@link java.nio.file.Path Path} implementations. * *

This implementation behaves almost identically to {@code sun.nio.fs.UnixPath}. The only * difference is that some methods (like {@link #relativize(UnixPath)} go to greater lengths to * preserve trailing backslashes, in order to ensure the path will continue to be recognized as a * directory. * *

Note: This code might not play nice with Supplementary * Characters as Surrogates. */ @Immutable final class UnixPath implements CharSequence { public static final char DOT = '.'; public static final char SEPARATOR = '/'; public static final String ROOT = "" + SEPARATOR; public static final String CURRENT_DIR = "" + DOT; public static final String PARENT_DIR = "" + DOT + DOT; public static final UnixPath EMPTY_PATH = new UnixPath(false, ""); public static final UnixPath ROOT_PATH = new UnixPath(false, ROOT); private static final Splitter SPLITTER = Splitter.on(SEPARATOR).omitEmptyStrings(); private static final Splitter SPLITTER_PERMIT_EMPTY_COMPONENTS = Splitter.on(SEPARATOR); private static final Joiner JOINER = Joiner.on(SEPARATOR); private static final Ordering> ORDERING = Ordering.natural().lexicographical(); private final String path; private List lazyStringParts; private final boolean permitEmptyComponents; private UnixPath(boolean permitEmptyComponents, String path) { this.path = checkNotNull(path); this.permitEmptyComponents = permitEmptyComponents; } /** Returns new path of {@code first}. */ public static UnixPath getPath(boolean permitEmptyComponents, String path) { if (path.isEmpty()) { return EMPTY_PATH; } else if (isRootInternal(path)) { return ROOT_PATH; } else { return new UnixPath(permitEmptyComponents, path); } } /** * Returns new path of {@code first} with {@code more} components resolved against it. * * @see #resolve(UnixPath) * @see java.nio.file.FileSystem#getPath(String, String...) */ public static UnixPath getPath(boolean permitEmptyComponents, String first, String... more) { if (more.length == 0) { return getPath(permitEmptyComponents, first); } StringBuilder builder = new StringBuilder(first); for (int i = 0; i < more.length; i++) { String part = more[i]; if (part.isEmpty()) { continue; } else if (isAbsoluteInternal(part)) { if (i == more.length - 1) { return new UnixPath(permitEmptyComponents, part); } else { builder.replace(0, builder.length(), part); } } else if (hasTrailingSeparatorInternal(builder)) { builder.append(part); } else { builder.append(SEPARATOR); builder.append(part); } } return new UnixPath(permitEmptyComponents, builder.toString()); } /** Returns {@code true} consists only of {@code separator}. */ public boolean isRoot() { return isRootInternal(path); } private static boolean isRootInternal(String path) { return path.length() == 1 && path.charAt(0) == SEPARATOR; } /** Returns {@code true} if path starts with {@code separator}. */ public boolean isAbsolute() { return isAbsoluteInternal(path); } private static boolean isAbsoluteInternal(String path) { return !path.isEmpty() && path.charAt(0) == SEPARATOR; } /** Returns {@code true} if path ends with {@code separator}. */ public boolean hasTrailingSeparator() { return hasTrailingSeparatorInternal(path); } private static boolean hasTrailingSeparatorInternal(CharSequence path) { return path.length() != 0 && path.charAt(path.length() - 1) == SEPARATOR; } /** Returns {@code true} if path ends with a trailing slash, or would after normalization. */ public boolean seemsLikeADirectory() { int length = path.length(); return path.isEmpty() || path.charAt(length - 1) == SEPARATOR || path.endsWith(".") && (length == 1 || path.charAt(length - 2) == SEPARATOR) || path.endsWith("..") && (length == 2 || path.charAt(length - 3) == SEPARATOR); } /** * Returns last component in {@code path}. * * @see java.nio.file.Path#getFileName() */ @Nullable public UnixPath getFileName() { if (path.isEmpty()) { return EMPTY_PATH; } else if (isRoot()) { return null; } else { List parts = getParts(); String last = parts.get(parts.size() - 1); return parts.size() == 1 && path.equals(last) ? this : new UnixPath(permitEmptyComponents, last); } } /** * Returns parent directory (including trailing separator) or {@code null} if no parent remains. * * @see java.nio.file.Path#getParent() */ @Nullable public UnixPath getParent() { if (path.isEmpty() || isRoot()) { return null; } int index = hasTrailingSeparator() ? path.lastIndexOf(SEPARATOR, path.length() - 2) : path.lastIndexOf(SEPARATOR); if (index == -1) { return isAbsolute() ? ROOT_PATH : null; } else { return new UnixPath(permitEmptyComponents, path.substring(0, index + 1)); } } /** * Returns root component if an absolute path, otherwise {@code null}. * * @see java.nio.file.Path#getRoot() */ @Nullable public UnixPath getRoot() { return isAbsolute() ? ROOT_PATH : null; } /** * Returns specified range of sub-components in path joined together. * * @see java.nio.file.Path#subpath(int, int) */ public UnixPath subpath(int beginIndex, int endIndex) { if (path.isEmpty() && beginIndex == 0 && endIndex == 1) { return this; } checkArgument(beginIndex >= 0 && endIndex > beginIndex); List subList; try { subList = getParts().subList(beginIndex, endIndex); } catch (IndexOutOfBoundsException e) { throw new IllegalArgumentException(); } return new UnixPath(permitEmptyComponents, JOINER.join(subList)); } /** * Returns number of components in {@code path}. * * @see java.nio.file.Path#getNameCount() */ public int getNameCount() { if (path.isEmpty()) { return 1; } else if (isRoot()) { return 0; } else { return getParts().size(); } } /** * Returns component in {@code path} at {@code index}. * * @see java.nio.file.Path#getName(int) */ public UnixPath getName(int index) { if (path.isEmpty()) { return this; } try { return new UnixPath(permitEmptyComponents, getParts().get(index)); } catch (IndexOutOfBoundsException e) { throw new IllegalArgumentException(); } } /** * Returns path without extra separators or {@code .} and {@code ..}, preserving trailing slash. * * @see java.nio.file.Path#normalize() */ public UnixPath normalize() { List parts = new ArrayList<>(); boolean mutated = false; int resultLength = 0; int mark = 0; int index; do { index = path.indexOf(SEPARATOR, mark); String part = path.substring(mark, index == -1 ? path.length() : index + 1); switch (part) { case CURRENT_DIR: case CURRENT_DIR + SEPARATOR: mutated = true; break; case PARENT_DIR: case PARENT_DIR + SEPARATOR: mutated = true; if (!parts.isEmpty()) { resultLength -= parts.remove(parts.size() - 1).length(); } break; default: if (index != mark || index == 0) { parts.add(part); resultLength = part.length(); } else { mutated = true; } } mark = index + 1; } while (index != -1); if (!mutated) { return this; } StringBuilder result = new StringBuilder(resultLength); for (String part : parts) { result.append(part); } return new UnixPath(permitEmptyComponents, result.toString()); } /** * Returns {@code other} appended to {@code path}. * * @see java.nio.file.Path#resolve(java.nio.file.Path) */ public UnixPath resolve(UnixPath other) { if (other.path.isEmpty()) { return this; } else if (other.isAbsolute()) { return other; } else if (hasTrailingSeparator()) { return new UnixPath(permitEmptyComponents, path + other.path); } else { return new UnixPath(permitEmptyComponents, path + SEPARATOR + other.path); } } /** * Returns {@code other} resolved against parent of {@code path}. * * @see java.nio.file.Path#resolveSibling(java.nio.file.Path) */ public UnixPath resolveSibling(UnixPath other) { checkNotNull(other); UnixPath parent = getParent(); return parent == null ? other : parent.resolve(other); } /** * Returns {@code other} made relative to {@code path}. * * @see java.nio.file.Path#relativize(java.nio.file.Path) */ public UnixPath relativize(UnixPath other) { checkArgument(isAbsolute() == other.isAbsolute(), "'other' is different type of Path"); if (path.isEmpty()) { return other; } PeekingIterator left = Iterators.peekingIterator(split()); PeekingIterator right = Iterators.peekingIterator(other.split()); while (left.hasNext() && right.hasNext()) { if (!left.peek().equals(right.peek())) { break; } left.next(); right.next(); } StringBuilder result = new StringBuilder(path.length() + other.path.length()); while (left.hasNext()) { result.append(PARENT_DIR); result.append(SEPARATOR); left.next(); } while (right.hasNext()) { result.append(right.next()); result.append(SEPARATOR); } if (result.length() > 0 && !other.hasTrailingSeparator()) { result.deleteCharAt(result.length() - 1); } return new UnixPath(permitEmptyComponents, result.toString()); } /** * Returns {@code true} if {@code path} starts with {@code other}. * * @see java.nio.file.Path#startsWith(java.nio.file.Path) */ public boolean startsWith(UnixPath other) { UnixPath me = removeTrailingSeparator(); other = other.removeTrailingSeparator(); if (other.path.length() > me.path.length()) { return false; } else if (me.isAbsolute() != other.isAbsolute()) { return false; } else if (!me.path.isEmpty() && other.path.isEmpty()) { return false; } return startsWith(split(), other.split()); } private static boolean startsWith(Iterator lefts, Iterator rights) { while (rights.hasNext()) { if (!lefts.hasNext() || !rights.next().equals(lefts.next())) { return false; } } return true; } /** * Returns {@code true} if {@code path} ends with {@code other}. * * @see java.nio.file.Path#endsWith(java.nio.file.Path) */ public boolean endsWith(UnixPath other) { UnixPath me = removeTrailingSeparator(); other = other.removeTrailingSeparator(); if (other.path.length() > me.path.length()) { return false; } else if (!me.path.isEmpty() && other.path.isEmpty()) { return false; } else if (other.isAbsolute()) { return me.isAbsolute() && me.path.equals(other.path); } return startsWith(me.splitReverse(), other.splitReverse()); } /** * Compares two paths lexicographically for ordering. * * @see java.nio.file.Path#compareTo(java.nio.file.Path) */ public int compareTo(UnixPath other) { return ORDERING.compare(getParts(), other.getParts()); } /** Converts relative path to an absolute path. */ public UnixPath toAbsolutePath(UnixPath currentWorkingDirectory) { checkArgument(currentWorkingDirectory.isAbsolute()); return isAbsolute() ? this : currentWorkingDirectory.resolve(this); } /** Returns {@code toAbsolutePath(ROOT_PATH)}. */ public UnixPath toAbsolutePath() { return toAbsolutePath(ROOT_PATH); } /** Removes beginning separator from path, if an absolute path. */ public UnixPath removeBeginningSeparator() { return isAbsolute() ? new UnixPath(permitEmptyComponents, path.substring(1)) : this; } /** Adds trailing separator to path, if it isn't present. */ public UnixPath addTrailingSeparator() { return hasTrailingSeparator() ? this : new UnixPath(permitEmptyComponents, path + SEPARATOR); } /** Removes trailing separator from path, unless it's root. */ public UnixPath removeTrailingSeparator() { if (!isRoot() && hasTrailingSeparator()) { return new UnixPath(permitEmptyComponents, path.substring(0, path.length() - 1)); } else { return this; } } /** Splits path into components, excluding separators and empty strings. */ public Iterator split() { return getParts().iterator(); } /** Splits path into components in reverse, excluding separators and empty strings. */ public Iterator splitReverse() { return Lists.reverse(getParts()).iterator(); } @Override public boolean equals(Object other) { return this == other || other instanceof UnixPath && path.equals(((UnixPath) other).path); } @Override public int hashCode() { return path.hashCode(); } /** Returns path as a string. */ @Override public String toString() { return path; } @Override public int length() { return path.length(); } @Override public char charAt(int index) { return path.charAt(index); } @Override public CharSequence subSequence(int start, int end) { return path.subSequence(start, end); } /** Returns {@code true} if this path is an empty string. */ public boolean isEmpty() { return path.isEmpty(); } /** Returns list of path components, excluding slashes. */ private List getParts() { List result = lazyStringParts; return result != null ? result : (lazyStringParts = path.isEmpty() || isRoot() ? Collections.emptyList() : createParts()); } private List createParts() { if (permitEmptyComponents) { return SPLITTER_PERMIT_EMPTY_COMPONENTS.splitToList( path.charAt(0) == SEPARATOR ? path.substring(1) : path); } else { return SPLITTER.splitToList(path); } } }





© 2015 - 2024 Weber Informatics LLC | Privacy Policy