com.google.jimfs.PathService.java Source code

Java tutorial

Introduction

Here is the source code for com.google.jimfs.PathService.java

Source

/*
 * 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.checkArgument;
import static com.google.common.base.Preconditions.checkNotNull;
import static com.google.common.base.Preconditions.checkState;
import static com.google.jimfs.PathType.ParseResult;

import com.google.common.annotations.VisibleForTesting;
import com.google.common.base.Functions;
import com.google.common.base.Predicate;
import com.google.common.collect.ComparisonChain;
import com.google.common.collect.ImmutableList;
import com.google.common.collect.ImmutableSet;
import com.google.common.collect.Iterables;
import com.google.common.collect.Lists;
import com.google.common.collect.Ordering;

import java.net.URI;
import java.nio.file.FileSystem;
import java.nio.file.PathMatcher;
import java.util.ArrayList;
import java.util.Comparator;
import java.util.List;

import javax.annotation.Nullable;

/**
 * Service for creating {@link JimfsPath} instances and handling other path-related operations.
 *
 * @author Colin Decker
 */
final class PathService implements Comparator<JimfsPath> {

    private static final Ordering<Name> DISPLAY_ROOT_ORDERING = Name.displayOrdering().nullsLast();
    private static final Ordering<Iterable<Name>> DISPLAY_NAMES_ORDERING = Name.displayOrdering().lexicographical();

    private static final Ordering<Name> CANONICAL_ROOT_ORDERING = Name.canonicalOrdering().nullsLast();
    private static final Ordering<Iterable<Name>> CANONICAL_NAMES_ORDERING = Name.canonicalOrdering()
            .lexicographical();

    private final PathType type;

    private final ImmutableSet<PathNormalization> displayNormalizations;
    private final ImmutableSet<PathNormalization> canonicalNormalizations;
    private final boolean equalityUsesCanonicalForm;

    private final Ordering<Name> rootOrdering;
    private final Ordering<Iterable<Name>> namesOrdering;

    private volatile FileSystem fileSystem;
    private volatile JimfsPath emptyPath;

    PathService(Configuration config) {
        this(config.pathType, config.nameDisplayNormalization, config.nameCanonicalNormalization,
                config.pathEqualityUsesCanonicalForm);
    }

    PathService(PathType type, Iterable<PathNormalization> displayNormalizations,
            Iterable<PathNormalization> canonicalNormalizations, boolean equalityUsesCanonicalForm) {
        this.type = checkNotNull(type);
        this.displayNormalizations = ImmutableSet.copyOf(displayNormalizations);
        this.canonicalNormalizations = ImmutableSet.copyOf(canonicalNormalizations);
        this.equalityUsesCanonicalForm = equalityUsesCanonicalForm;

        this.rootOrdering = equalityUsesCanonicalForm ? CANONICAL_ROOT_ORDERING : DISPLAY_ROOT_ORDERING;
        this.namesOrdering = equalityUsesCanonicalForm ? CANONICAL_NAMES_ORDERING : DISPLAY_NAMES_ORDERING;
    }

    /**
     * Sets the file system to use for created paths.
     */
    public void setFileSystem(FileSystem fileSystem) {
        // allowed to not be JimfsFileSystem for testing purposes only
        checkState(this.fileSystem == null, "may not set fileSystem twice");
        this.fileSystem = checkNotNull(fileSystem);
    }

    /**
     * Returns the file system this service is for.
     */
    public FileSystem getFileSystem() {
        return fileSystem;
    }

    /**
     * Returns the default path separator.
     */
    public String getSeparator() {
        return type.getSeparator();
    }

    /**
     * Returns an empty path which has a single name, the empty string.
     */
    public JimfsPath emptyPath() {
        JimfsPath result = emptyPath;
        if (result == null) {
            // use createPathInternal to avoid recursive call from createPath()
            result = createPathInternal(null, ImmutableList.of(Name.EMPTY));
            emptyPath = result;
            return result;
        }
        return result;
    }

    /**
     * Returns the {@link Name} form of the given string.
     */
    public Name name(String name) {
        switch (name) {
        case "":
            return Name.EMPTY;
        case ".":
            return Name.SELF;
        case "..":
            return Name.PARENT;
        default:
            String display = PathNormalization.normalize(name, displayNormalizations);
            String canonical = PathNormalization.normalize(name, canonicalNormalizations);
            return Name.create(display, canonical);
        }
    }

    /**
     * Returns the {@link Name} forms of the given strings.
     */
    @VisibleForTesting
    List<Name> names(Iterable<String> names) {
        List<Name> result = new ArrayList<>();
        for (String name : names) {
            result.add(name(name));
        }
        return result;
    }

    /**
     * Returns a root path with the given name.
     */
    public JimfsPath createRoot(Name root) {
        return createPath(checkNotNull(root), ImmutableList.<Name>of());
    }

    /**
     * Returns a single filename path with the given name.
     */
    public JimfsPath createFileName(Name name) {
        return createPath(null, ImmutableList.of(name));
    }

    /**
     * Returns a relative path with the given names.
     */
    public JimfsPath createRelativePath(Iterable<Name> names) {
        return createPath(null, ImmutableList.copyOf(names));
    }

    /**
     * Returns a path with the given root (or no root, if null) and the given names.
     */
    public JimfsPath createPath(@Nullable Name root, Iterable<Name> names) {
        ImmutableList<Name> nameList = ImmutableList.copyOf(Iterables.filter(names, NOT_EMPTY));
        if (root == null && nameList.isEmpty()) {
            // ensure the canonical empty path (one empty string name) is used rather than a path with
            // no root and no names
            return emptyPath();
        }
        return createPathInternal(root, nameList);
    }

    /**
     * Returns a path with the given root (or no root, if null) and the given names.
     */
    protected final JimfsPath createPathInternal(@Nullable Name root, Iterable<Name> names) {
        return new JimfsPath(this, root, names);
    }

    /**
     * Parses the given strings as a path.
     */
    public JimfsPath parsePath(String first, String... more) {
        String joined = type.joiner().join(Iterables.filter(Lists.asList(first, more), NOT_EMPTY));
        return toPath(type.parsePath(joined));
    }

    private JimfsPath toPath(ParseResult parsed) {
        Name root = parsed.root() == null ? null : name(parsed.root());
        Iterable<Name> names = names(parsed.names());
        return createPath(root, names);
    }

    /**
     * Returns the string form of the given path.
     */
    public String toString(JimfsPath path) {
        Name root = path.root();
        String rootString = root == null ? null : root.toString();
        Iterable<String> names = Iterables.transform(path.names(), Functions.toStringFunction());
        return type.toString(rootString, names);
    }

    /**
     * Creates a hash code for the given path.
     */
    public int hash(JimfsPath path) {
        int hash = 31;
        hash = 31 * hash + getFileSystem().hashCode();

        final Name root = path.root();
        final ImmutableList<Name> names = path.names();

        if (equalityUsesCanonicalForm) {
            // use hash codes of names themselves, which are based on the canonical form
            hash = 31 * hash + (root == null ? 0 : root.hashCode());
            for (Name name : names) {
                hash = 31 * hash + name.hashCode();
            }
        } else {
            // use hash codes from toString() form of names
            hash = 31 * hash + (root == null ? 0 : root.toString().hashCode());
            for (Name name : names) {
                hash = 31 * hash + name.toString().hashCode();
            }
        }
        return hash;
    }

    @Override
    public int compare(JimfsPath a, JimfsPath b) {
        return ComparisonChain.start().compare(a.root(), b.root(), rootOrdering)
                .compare(a.names(), b.names(), namesOrdering).result();
    }

    /**
     * Returns the URI for the given path. The given file system URI is the base against which the
     * path is resolved to create the returned URI.
     */
    public URI toUri(URI fileSystemUri, JimfsPath path) {
        checkArgument(path.isAbsolute(), "path (%s) must be absolute", path);
        String root = String.valueOf(path.root());
        Iterable<String> names = Iterables.transform(path.names(), Functions.toStringFunction());
        return type.toUri(fileSystemUri, root, names);
    }

    /**
     * Converts the path of the given URI into a path for this file system.
     */
    public JimfsPath fromUri(URI uri) {
        return toPath(type.fromUri(uri));
    }

    /**
     * Returns a {@link PathMatcher} for the given syntax and pattern as specified by
     * {@link FileSystem#getPathMatcher(String)}.
     */
    public PathMatcher createPathMatcher(String syntaxAndPattern) {
        return PathMatchers.getPathMatcher(syntaxAndPattern, type.getSeparator() + type.getOtherSeparators(),
                displayNormalizations);
    }

    private static final Predicate<Object> NOT_EMPTY = new Predicate<Object>() {
        @Override
        public boolean apply(Object input) {
            return !input.toString().isEmpty();
        }
    };
}