Java tutorial
/* * Copyright 2013 Google 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.google.jimfs; 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.ImmutableList; import com.google.common.collect.Iterables; import java.net.URI; import java.net.URISyntaxException; import java.nio.file.InvalidPathException; import java.util.Arrays; import javax.annotation.Nullable; /** * An object defining a specific type of path. Knows how to parse strings to a path and how to * render a path as a string as well as what the path separator is and what other separators are * recognized when parsing paths. * * @author Colin Decker */ public abstract class PathType { /** * Returns a Unix-style path type. "/" is both the root and the only separator. Any path starting * with "/" is considered absolute. The nul character ('\0') is disallowed in paths. */ public static PathType unix() { return UnixPathType.INSTANCE; } /** * Returns a Windows-style path type. The canonical separator character is "\". "/" is also * treated as a separator when parsing paths. * * <p>As much as possible, this implementation follows the information provided in * <a href="http://msdn.microsoft.com/en-us/library/windows/desktop/aa365247(v=vs.85).aspx"> * this article</a>. Paths with drive-letter roots (e.g. "C:\") and paths with UNC roots (e.g. * "\\host\share\") are supported. * * <p>Two Windows path features are not currently supported as they are too Windows-specific: * * <ul> * <li>Relative paths containing a drive-letter root, for example "C:" or "C:foo\bar". Such * paths have a root component and optionally have names, but are <i>relative</i> paths, * relative to the working directory of the drive identified by the root.</li> * <li>Absolute paths with no root, for example "\foo\bar". Such paths are absolute paths on * the current drive.</li> * </ul> */ public static PathType windows() { return WindowsPathType.INSTANCE; } private final boolean allowsMultipleRoots; private final String separator; private final String otherSeparators; private final Joiner joiner; private final Splitter splitter; protected PathType(boolean allowsMultipleRoots, char separator, char... otherSeparators) { this.separator = String.valueOf(separator); this.allowsMultipleRoots = allowsMultipleRoots; this.otherSeparators = String.valueOf(otherSeparators); this.joiner = Joiner.on(separator); this.splitter = createSplitter(separator, otherSeparators); } private static final char[] regexReservedChars = "^$.?+*\\[]{}()".toCharArray(); static { Arrays.sort(regexReservedChars); } private static boolean isRegexReserved(char c) { return Arrays.binarySearch(regexReservedChars, c) >= 0; } private static Splitter createSplitter(char separator, char... otherSeparators) { if (otherSeparators.length == 0) { return Splitter.on(separator).omitEmptyStrings(); } // TODO(cgdecker): When CharMatcher is out of @Beta, us Splitter.on(CharMatcher) StringBuilder patternBuilder = new StringBuilder(); patternBuilder.append("["); appendToRegex(separator, patternBuilder); for (char other : otherSeparators) { appendToRegex(other, patternBuilder); } patternBuilder.append("]"); return Splitter.onPattern(patternBuilder.toString()).omitEmptyStrings(); } private static void appendToRegex(char separator, StringBuilder patternBuilder) { if (isRegexReserved(separator)) { patternBuilder.append("\\"); } patternBuilder.append(separator); } /** * Returns whether or not this type of path allows multiple root directories. */ public final boolean allowsMultipleRoots() { return allowsMultipleRoots; } /** * Returns the canonical separator for this path type. The returned string always has a length of * one. */ public final String getSeparator() { return separator; } /** * Returns the other separators that are recognized when parsing a path. If no other separators * are recognized, the empty string is returned. */ public final String getOtherSeparators() { return otherSeparators; } /** * Returns the path joiner for this path type. */ public final Joiner joiner() { return joiner; } /** * Returns the path splitter for this path type. */ public final Splitter splitter() { return splitter; } /** * Returns an empty path. */ protected final ParseResult emptyPath() { return new ParseResult(null, ImmutableList.of("")); } /** * Parses the given strings as a path. * * @throws InvalidPathException if the path isn't valid for this path type */ public abstract ParseResult parsePath(String path); /** * Returns the string form of the given path. */ public abstract String toString(@Nullable String root, Iterable<String> names); /** * Returns the string form of the given path for use in the path part of a URI. The root element * is not nullable as the path must be absolute. The elements of the returned path <i>do not</i> * need to be escaped. */ protected abstract String toUriPath(String root, Iterable<String> names); /** * Parses a path from the given URI path. * * @throws InvalidPathException if the given path isn't valid for this path type */ protected abstract ParseResult parseUriPath(String uriPath); /** * Creates a URI for the path with the given root and names in the file system with the given URI. */ public final URI toUri(URI fileSystemUri, String root, Iterable<String> names) { String path = toUriPath(root, names); try { // it should not suck this much to create a new URI that's the same except with a path set =( // need to do it this way for automatic path escaping return new URI(fileSystemUri.getScheme(), fileSystemUri.getUserInfo(), fileSystemUri.getHost(), fileSystemUri.getPort(), path, null, null); } catch (URISyntaxException e) { throw new AssertionError(e); } } /** * Parses a path from the given URI. */ public final ParseResult fromUri(URI uri) { return parseUriPath(uri.getPath()); } /** * Simple result of parsing a path. */ public static final class ParseResult { @Nullable private final String root; private final Iterable<String> names; public ParseResult(@Nullable String root, Iterable<String> names) { this.root = root; this.names = checkNotNull(names); } /** * Returns whether or not this result is an absolute path. */ public boolean isAbsolute() { return root != null; } /** * Returns whether or not this result represents a root path. */ public boolean isRoot() { return root != null && Iterables.isEmpty(names); } /** * Returns the parsed root element, or null if there was no root. */ @Nullable public String root() { return root; } /** * Returns the parsed name elements. */ public Iterable<String> names() { return names; } } }