package com.github.marschall.memoryfilesystem;
import static java.lang.Math.min;
import java.nio.file.InvalidPathException;
import java.nio.file.PathMatcher;
import java.util.ArrayList;
import java.util.List;
import java.util.Map;
final class MultipleNamedRootsPathParser extends PathParser {
private final StringTransformer pathTransformer;
MultipleNamedRootsPathParser(String separator, StringTransformer pathTransformer, CharacterSet forbiddenCharacters) {
super(separator, forbiddenCharacters);
this.pathTransformer = pathTransformer;
}
@Override
AbstractPath parseUri(Map<String, Root> rootByKey, String uri) {
for (int i = 0; i < uri.length(); ++i) {
if (uri.charAt(i) != '/') {
return this.parse(rootByKey, uri.substring(i), EMPTY);
}
}
throw new InvalidPathException(uri, "not a valid path: ");
}
@Override
public AbstractPath parse(Map<String, Root> roots, String first, String... more) {
if (this.startWithSeparator(first, more)) {
// TODO build string
throw new InvalidPathException(first, "path must not start with separator", 1);
}
// REVIEW implement #count() to correctly set initial size
List<String> elements = new ArrayList<>();
this.parseInto(first, elements);
if (more != null && more.length > 0) {
for (String s : more) {
this.parseInto(s, elements);
}
}
MemoryFileSystem memoryFileSystem = this.getFileSystem(roots);
if (this.isAbsolute(elements)) {
Root root = this.getRoot(memoryFileSystem, roots, elements);
elements = elements.subList(1, elements.size());
this.check(elements);
return AbstractPath.createAboslute(memoryFileSystem, root, elements);
} else {
this.check(elements);
return AbstractPath.createRelative(memoryFileSystem, elements);
}
}
@Override
PathMatcher parseGlob(String pattern) {
if (this.startWithSeparator(pattern)) {
// TODO build string
throw new InvalidPathException(pattern, "path must not start with separator", 1);
}
// REVIEW implement #count() to correctly set initial size
List<String> elements = new ArrayList<>();
this.parseInto(pattern, elements);
if (this.isAbsolute(elements)) {
elements = elements.subList(1, elements.size());
return new GlobPathMatcher(true, convertToMatches(elements));
} else {
return new GlobPathMatcher(false, convertToMatches(elements));
}
}
private MemoryFileSystem getFileSystem(Map<String, Root> roots) {
return roots.values().iterator().next().getMemoryFileSystem();
}
private Root getRoot(MemoryFileSystem memoryFileSystem, Map<String, Root> roots, List<String> elements) {
String first = elements.get(0);
String key = this.pathTransformer.transform(first.substring(0, 1)); // C: -> C
Root root = roots.get(key);
if (root != null) {
return root;
} else {
// create a fake root that is not really in the file system
return new NamedRoot(memoryFileSystem, key);
}
}
private boolean isAbsolute(List<String> elements) {
if (elements.isEmpty()) {
return false;
}
String first = elements.get(0);
if (first.length() != 2 || first.charAt(1) != ':') {
return false;
}
return true;
}
private void parseInto(String s, List<String> elements) {
if (s.isEmpty()) {
return;
}
int fromIndex = 0;
int slashIndex = s.indexOf('/', fromIndex);
int separatorIndex = s.indexOf(this.separator, fromIndex);
int nextIndex = this.computeNextIndex(slashIndex, separatorIndex);
while (nextIndex != -1) {
if (nextIndex > fromIndex) {
// avoid empty strings for things like //
elements.add(s.substring(fromIndex, nextIndex));
}
fromIndex = nextIndex + 1;
if (slashIndex == -1) {
slashIndex = s.indexOf(this.separator, separatorIndex + 1);
nextIndex = slashIndex;
} else if (separatorIndex == -1) {
separatorIndex = s.indexOf('/', slashIndex + 1);
nextIndex = separatorIndex;
} else {
if (slashIndex < separatorIndex) {
slashIndex = s.indexOf('/', slashIndex + 1);
} else {
// they can not be equal
separatorIndex = s.indexOf(this.separator, separatorIndex + 1);
}
nextIndex = this.computeNextIndex(slashIndex, separatorIndex);
}
}
if (fromIndex < s.length()) {
elements.add(s.substring(fromIndex));
}
}
private int computeNextIndex(int slashIndex, int separatorIndex) {
if (slashIndex == -1) {
return separatorIndex;
} else if (separatorIndex == -1) {
return slashIndex;
} else {
return min(slashIndex, separatorIndex);
}
}
}