blob: 8e6a8fb1da5e59d7752ddae243df92efb85887d0 [file] [log] [blame]
/*
* 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());
}
}
}