package com.github.marschall.memoryfilesystem;
import static java.nio.file.StandardWatchEventKinds.ENTRY_CREATE;
import static java.nio.file.StandardWatchEventKinds.ENTRY_DELETE;
import static java.nio.file.StandardWatchEventKinds.ENTRY_MODIFY;
import java.io.File;
import java.io.IOException;
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.Collections;
import java.util.HashSet;
import java.util.List;
import java.util.Set;
abstract class AbstractPath implements Path {
// TODO think about #isEmpty to replace the instanceof checks
// TODO think about a visitor (visitRelative visitEmpty visitRoot) to replace instanceof checks
private final MemoryFileSystem fileSystem;
AbstractPath(MemoryFileSystem fileSystem) {
this.fileSystem = fileSystem;
}
static AbstractPath createAboslute(MemoryFileSystem fileSystem, Root root, List<String> nameElements) {
if (root == null) {
throw new IllegalArgumentException("root must not be null");
}
if (nameElements.isEmpty()) {
return root;
} else {
return new AbsolutePath(fileSystem, root, nameElements);
}
}
static AbstractPath createAboslute(MemoryFileSystem fileSystem, Root root, String nameElement) {
return createAboslute(fileSystem, root, Collections.singletonList(nameElement));
}
static AbstractPath createRelative(MemoryFileSystem fileSystem, List<String> nameElements) {
int nameElementCount = nameElements.size();
if (nameElementCount == 0) {
return fileSystem.getEmptyPath();
} else if (nameElementCount == 1) {
return createRelative(fileSystem, nameElements.get(0));
} else {
return new RelativePath(fileSystem, nameElements);
}
}
static AbstractPath createRelative(MemoryFileSystem fileSystem, String nameElement) {
return new SingletonPath(fileSystem, nameElement);
}
@Override
public MemoryFileSystem getFileSystem() {
return this.fileSystem;
}
MemoryFileSystem getMemoryFileSystem() {
return this.fileSystem;
}
@Override
public File toFile() {
throw new UnsupportedOperationException("memory file system does not support #toFile()");
}
@Override
public final boolean startsWith(Path other) {
if (!this.isSameFileSystem(other)) {
return false;
}
return this.startsWith((AbstractPath) other);
}
abstract boolean startsWith(AbstractPath other);
@Override
public final boolean endsWith(Path other) {
if (!this.isSameFileSystem(other)) {
return false;
}
return this.endsWith((AbstractPath) other);
}
abstract boolean endsWith(AbstractPath other);
@Override
public final Path resolve(Path other) {
this.assertSameFileSystem(other);
AbstractPath otherPath = (AbstractPath) other;
if (otherPath.isRoot()) {
return other;
} else if (other.isAbsolute()) {
// TODO totally unspecified, make configurable
return other;
} else if (otherPath.getNameCount() == 0) {
return this;
}
return this.resolve((ElementPath) otherPath);
}
abstract Path resolve(ElementPath other);
@Override
public final Path resolveSibling(Path other) {
this.assertSameFileSystem(other);
return this.resolveSibling((AbstractPath) other);
}
abstract Path resolveSibling(AbstractPath other);
abstract boolean isRoot();
@Override
public Path resolve(String other) {
return this.resolve(this.fileSystem.getPath(other));
}
@Override
public Path resolveSibling(String other) {
return this.resolveSibling(this.fileSystem.getPath(other));
}
@Override
public final Path relativize(Path other) {
this.assertSameFileSystem(other);
return this.relativize((AbstractPath) other);
}
abstract Path relativize(AbstractPath other);
private boolean isSameFileSystem(Path other) {
return other.getFileSystem() == this.getFileSystem();
}
private void assertSameFileSystem(Path other) {
if (!this.isSameFileSystem(other)) {
throw new ProviderMismatchException();
}
}
@Override
public int compareTo(Path other) {
if (this == other) {
return 0;
}
MemoryFileSystemProvider otherProvider = (MemoryFileSystemProvider) other.getFileSystem().provider();
if (otherProvider == null) {
// can't happen, just there to shut up compiler about unsed variable
throw new ClassCastException("no file system provider given");
}
AbstractPath otherPath = (AbstractPath) other;
String otherFileSystemKey = otherPath.getMemoryFileSystem().getKey();
String thisFileSystemKey = this.getMemoryFileSystem().getKey();
// always take case into account
int fileSystemComparison = thisFileSystemKey.compareTo(otherFileSystemKey);
if (fileSystemComparison != 0) {
return fileSystemComparison;
}
return this.compareTo(otherPath);
}
abstract int compareTo(AbstractPath other);
@Override
public WatchKey register(WatchService watcher, Kind<?>[] events, Modifier... modifiers) throws IOException {
// TODO report bug
// TODO java.nio.file.NotDirectoryException
if (true) {
throw new UnsupportedOperationException();
}
if (modifiers != null && modifiers.length > 0) {
throw new UnsupportedOperationException("no modifiers supported");
}
if (watcher == null) {
throw new NullPointerException("watcher is null");
}
// triggers class cast exception but this is API in other cases
MemoryFileSystemWatchService memoryWatcher = (MemoryFileSystemWatchService) watcher;
if (memoryWatcher.getMemoryFileSystem() != this.fileSystem) {
throw new IllegalArgumentException("watcher has to be from the same file system");
}
MemoryWatchKey watchKey = new MemoryWatchKey(this, memoryWatcher, this.asSet(events));
this.fileSystem.register(watchKey);
return watchKey;
}
private Set<Kind<?>> asSet(Kind<?>[] eventKinds) {
if (eventKinds == null) {
throw new NullPointerException("watcher is null");
}
int numberOfKinds = eventKinds.length;
if (numberOfKinds == 0) {
throw new IllegalArgumentException("no events specified");
}
if (numberOfKinds == 1) {
Kind<?> eventKind = eventKinds[0];
this.validate(eventKind);
return Collections.<Kind<?>>singleton(eventKind);
} else {
Set<Kind<?>> set = new HashSet<>(numberOfKinds);
for (Kind<?> kind : eventKinds) {
this.validate(kind);
set.add(kind);
}
return set;
}
}
private void validate(Kind<?> eventKind) {
if (eventKind != ENTRY_CREATE && eventKind != ENTRY_DELETE && eventKind != ENTRY_MODIFY) {
throw new UnsupportedOperationException("unsupported event kind: " + eventKind);
}
}
@Override
public WatchKey register(WatchService watcher, Kind<?>... events) throws IOException {
return this.register(watcher, events, (Modifier[]) null);
}
}