| /* |
| * Licensed to the Apache Software Foundation (ASF) under one or more |
| * contributor license agreements. See the NOTICE file distributed with |
| * this work for additional information regarding copyright ownership. |
| * The ASF licenses this file to You 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 org.apache.lucene.mockfile; |
| |
| import java.io.File; |
| import java.io.IOException; |
| import java.net.URI; |
| import java.nio.file.FileSystem; |
| import java.nio.file.LinkOption; |
| import java.nio.file.Path; |
| import java.nio.file.ProviderMismatchException; |
| import java.nio.file.WatchEvent.Kind; |
| import java.nio.file.WatchEvent.Modifier; |
| import java.nio.file.WatchKey; |
| import java.nio.file.WatchService; |
| import java.util.Iterator; |
| |
| import org.apache.lucene.util.SuppressForbidden; |
| |
| /** |
| * A {@code FilterPath} contains another |
| * {@code Path}, which it uses as its basic |
| * source of data, possibly transforming the data along the |
| * way or providing additional functionality. |
| */ |
| public class FilterPath implements Path { |
| |
| /** |
| * The underlying {@code Path} instance. |
| */ |
| protected final Path delegate; |
| |
| /** |
| * The parent {@code FileSystem} for this path. |
| */ |
| protected final FileSystem fileSystem; |
| |
| /** |
| * Construct a {@code FilterPath} with parent |
| * {@code fileSystem}, based on the specified base path. |
| * @param delegate specified base path. |
| * @param fileSystem parent fileSystem. |
| */ |
| public FilterPath(Path delegate, FileSystem fileSystem) { |
| this.delegate = delegate; |
| this.fileSystem = fileSystem; |
| } |
| |
| /** |
| * Get the underlying wrapped path. |
| * @return wrapped path. |
| */ |
| public Path getDelegate() { |
| return delegate; |
| } |
| |
| @Override |
| public FileSystem getFileSystem() { |
| return fileSystem; |
| } |
| |
| @Override |
| public boolean isAbsolute() { |
| return delegate.isAbsolute(); |
| } |
| |
| @Override |
| public Path getRoot() { |
| Path root = delegate.getRoot(); |
| if (root == null) { |
| return null; |
| } |
| return wrap(root); |
| } |
| |
| @Override |
| public Path getFileName() { |
| Path fileName = delegate.getFileName(); |
| if (fileName == null) { |
| return null; |
| } |
| return wrap(fileName); |
| } |
| |
| @Override |
| public Path getParent() { |
| Path parent = delegate.getParent(); |
| if (parent == null) { |
| return null; |
| } |
| return wrap(parent); |
| } |
| |
| @Override |
| public int getNameCount() { |
| return delegate.getNameCount(); |
| } |
| |
| @Override |
| public Path getName(int index) { |
| return wrap(delegate.getName(index)); |
| } |
| |
| @Override |
| public Path subpath(int beginIndex, int endIndex) { |
| return wrap(delegate.subpath(beginIndex, endIndex)); |
| } |
| |
| @Override |
| public boolean startsWith(Path other) { |
| return delegate.startsWith(toDelegate(other)); |
| } |
| |
| @Override |
| public boolean startsWith(String other) { |
| return delegate.startsWith(other); |
| } |
| |
| @Override |
| public boolean endsWith(Path other) { |
| return delegate.endsWith(toDelegate(other)); |
| } |
| |
| @Override |
| public boolean endsWith(String other) { |
| return delegate.startsWith(other); |
| } |
| |
| @Override |
| public Path normalize() { |
| return wrap(delegate.normalize()); |
| } |
| |
| @Override |
| public Path resolve(Path other) { |
| return wrap(delegate.resolve(toDelegate(other))); |
| } |
| |
| @Override |
| public Path resolve(String other) { |
| return wrap(delegate.resolve(other)); |
| } |
| |
| @Override |
| public Path resolveSibling(Path other) { |
| return wrap(delegate.resolveSibling(toDelegate(other))); |
| } |
| |
| @Override |
| public Path resolveSibling(String other) { |
| return wrap(delegate.resolveSibling(other)); |
| } |
| |
| @Override |
| public Path relativize(Path other) { |
| return wrap(delegate.relativize(toDelegate(other))); |
| } |
| |
| // TODO: should these methods not expose delegate result directly? |
| // it could allow code to "escape" the sandbox... |
| |
| @Override |
| public URI toUri() { |
| return delegate.toUri(); |
| } |
| |
| @Override |
| public String toString() { |
| return delegate.toString(); |
| } |
| |
| @Override |
| public Path toAbsolutePath() { |
| return wrap(delegate.toAbsolutePath()); |
| } |
| |
| @Override |
| public Path toRealPath(LinkOption... options) throws IOException { |
| return wrap(delegate.toRealPath(options)); |
| } |
| |
| @Override |
| @SuppressForbidden(reason = "Abstract API requires to use java.io.File") |
| public File toFile() { |
| // TODO: should we throw exception here? |
| return delegate.toFile(); |
| } |
| |
| @Override |
| public WatchKey register(WatchService watcher, Kind<?>[] events, Modifier... modifiers) throws IOException { |
| return delegate.register(watcher, events, modifiers); |
| } |
| |
| @Override |
| public WatchKey register(WatchService watcher, Kind<?>... events) throws IOException { |
| return delegate.register(watcher, events); |
| } |
| |
| @Override |
| public Iterator<Path> iterator() { |
| final Iterator<Path> iterator = delegate.iterator(); |
| return new Iterator<Path>() { |
| @Override |
| public boolean hasNext() { |
| return iterator.hasNext(); |
| } |
| |
| @Override |
| public Path next() { |
| return wrap(iterator.next()); |
| } |
| |
| @Override |
| public void remove() { |
| iterator.remove(); |
| } |
| }; |
| } |
| |
| @Override |
| public int compareTo(Path other) { |
| return delegate.compareTo(toDelegate(other)); |
| } |
| |
| @Override |
| public int hashCode() { |
| return delegate.hashCode(); |
| } |
| |
| @Override |
| public boolean equals(Object obj) { |
| if (this == obj) return true; |
| if (obj == null) return false; |
| if (getClass() != obj.getClass()) return false; |
| FilterPath other = (FilterPath) obj; |
| if (delegate == null) { |
| if (other.delegate != null) return false; |
| } else if (!delegate.equals(other.delegate)) return false; |
| if (fileSystem == null) { |
| if (other.fileSystem != null) return false; |
| } else if (!fileSystem.equals(other.fileSystem)) return false; |
| return true; |
| } |
| |
| /** |
| * Unwraps all {@code FilterPath}s, returning |
| * the innermost {@code Path}. |
| * <p> |
| * WARNING: this is exposed for testing only! |
| * @param path specified path. |
| * @return innermost Path instance |
| */ |
| public static Path unwrap(Path path) { |
| while (path instanceof FilterPath) { |
| path = ((FilterPath)path).delegate; |
| } |
| return path; |
| } |
| |
| /** Override this to customize the return wrapped |
| * path from various operations */ |
| protected Path wrap(Path other) { |
| return new FilterPath(other, fileSystem); |
| } |
| |
| /** Override this to customize the unboxing of Path |
| * from various operations |
| */ |
| protected Path toDelegate(Path path) { |
| if (path instanceof FilterPath) { |
| FilterPath fp = (FilterPath) path; |
| if (fp.fileSystem != fileSystem) { |
| throw new ProviderMismatchException("mismatch, expected: " + fileSystem.provider().getClass() + ", got: " + fp.fileSystem.provider().getClass()); |
| } |
| return fp.delegate; |
| } else { |
| throw new ProviderMismatchException("mismatch, expected: FilterPath, got: " + path.getClass()); |
| } |
| } |
| } |