package com.github.marschall.memoryfilesystem;
import java.net.URI;
import java.nio.file.Path;
import java.text.CollationKey;
import java.text.Collator;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.List;
final class RelativePath extends NonEmptyPath {
RelativePath(MemoryFileSystem fileSystem, List<String> nameElements) {
super(fileSystem, nameElements);
}
@Override
public boolean isAbsolute() {
return false;
}
@Override
public String toString() {
// TODO estimate size
StringBuilder buffer = new StringBuilder();
String separator = this.getFileSystem().getSeparator();
boolean first = true;
for (String element : this.getNameElements()) {
if (!first) {
buffer.append(separator);
}
buffer.append(element);
first = false;
}
return buffer.toString();
}
@Override
public Path getRoot() {
return null;
}
@Override
public Path getParent() {
if (this.getNameElements().size() == 1) {
return null;
} else {
List<String> subList = this.getNameElements().subList(0, this.getNameElements().size() - 1);
return createRelative(this.getMemoryFileSystem(), subList);
}
}
@Override
public Path getName(int index) {
int nameCount = this.getNameCount();
if (index < 0) {
throw new IllegalArgumentException("index must be positive but was " + index);
}
if (index >= nameCount) {
throw new IllegalArgumentException("index must not be bigger than " + (nameCount - 1) + " but was " + index);
}
if (nameCount == 1) {
return this;
} else {
return createRelative(this.getMemoryFileSystem(), this.getNameElements().get(index));
}
}
@Override
public Path subpath(int beginIndex, int endIndex) {
this.checkNameRange(beginIndex, endIndex);
if (endIndex - beginIndex == this.getNameCount()) {
return this;
} else {
return createRelative(this.getMemoryFileSystem(), this.getNameElements().subList(beginIndex, endIndex));
}
}
@Override
Path newInstance(MemoryFileSystem fileSystem, List<String> pathElements) {
return createRelative(this.getMemoryFileSystem(), pathElements);
}
@Override
public URI toUri() {
return this.toAbsolutePath().toUri();
}
@Override
public Path toAbsolutePath() {
return this.getMemoryFileSystem().getDefaultPath().resolve(this);
}
@Override
int compareToNonRoot(ElementPath other) {
if (other.isAbsolute()) {
return 1;
}
if (other.getNameCount() == 0) {
return 1;
}
return this.compareNameElements(other.getNameElements());
}
@Override
boolean startsWith(AbstractPath other) {
if (other.isAbsolute()) {
return false;
}
if (other.isRoot()) {
return false;
}
if (other instanceof ElementPath) {
ElementPath otherPath = (ElementPath) other;
int otherNameCount = otherPath.getNameCount();
if (otherNameCount == 0) {
// empty path
return false;
}
if (otherNameCount > this.getNameCount()) {
return false;
}
// otherNameCount smaller or equal to this.getNameCount()
Collator collator = this.getMemoryFileSystem().getCollator();
for (int i = 0; i < otherNameCount; ++i) {
String thisElement = this.getNameElement(i);
String otherElement = otherPath.getNameElement(i);
if (!collator.equals(thisElement, otherElement)) {
return false;
}
}
return true;
} else {
throw new IllegalArgumentException("can't check for #startsWith against " + other);
}
}
@Override
boolean endsWith(AbstractPath other) {
if (other.isAbsolute()) {
return false;
}
if (other.isRoot()) {
return false;
}
if (other instanceof ElementPath) {
return this.endsWithRelativePath(other);
} else {
throw new IllegalArgumentException("can't check for #startsWith against " + other);
}
}
@Override
Path resolve(ElementPath other) {
List<String> resolvedElements = CompositeList.create(this.getNameElements(), other.getNameElements());
return createRelative(this.getMemoryFileSystem(), resolvedElements);
}
@Override
Path resolveSibling(AbstractPath other) {
if (other.isAbsolute()) {
return other;
}
if (other instanceof ElementPath) {
ElementPath otherPath = (ElementPath) other;
List<String> resolvedElements = CompositeList.create(this.getNameElements().subList(0, this.getNameCount() - 1), otherPath.getNameElements());
return createRelative(this.getMemoryFileSystem(), resolvedElements);
} else {
throw new IllegalArgumentException("can't resolve" + other);
}
}
@Override
Path relativize(AbstractPath other) {
if (other.isAbsolute()) {
// only support relativization against relative paths
throw new IllegalArgumentException("can only relativize a relative path against a relative path");
}
if (other instanceof ElementPath) {
// normal case
return this.buildRelativePathAgainst(other);
} else {
// unknown case
throw new IllegalArgumentException("unsupported path argument");
}
}
@Override
public boolean equals(Object obj) {
if (obj == this) {
return true;
}
if (!(obj instanceof RelativePath)) {
return false;
}
ElementPath other = (ElementPath) obj;
// compareTo take memory file system key into account to ensure
// a.compareTo(b) == 0 iff a.equals(b)
return this.getMemoryFileSystem().equals(other.getMemoryFileSystem())
&& this.equalElementsAs(other.getNameElements());
}
@Override
public int hashCode() {
MemoryFileSystem memoryFileSystem = this.getMemoryFileSystem();
Collator collator = memoryFileSystem.getCollator();
int result = 17;
result = 31 * result + memoryFileSystem.hashCode();
for (String each : this.getNameElements()) {
// TODO expensive, safe
CollationKey collationKey = collator.getCollationKey(each);
result = 31 * result + Arrays.hashCode(collationKey.toByteArray());
}
return result;
}
@Override
protected List<String> handleDotDotNormalizationNotYetModified(List<String> nameElements,
int nameElementsSize, int i) {
// copy everything preceding the element before ".." unless it's ".."
if (i > 0 && !nameElements.get(i - 1).equals("..")) {
List<String> normalized = new ArrayList<>(nameElementsSize - 1);
if (i > 1) {
normalized.addAll(nameElements.subList(0, i - 1));
}
return normalized;
} else {
return nameElements;
}
}
@Override
protected void handleDotDotNormalizationAlreadyModified(List<String> normalized) {
int lastIndex = normalized.size() - 1;
if (!normalized.get(lastIndex).equals("..")) {
// "../.." has to be preserved
normalized.remove(lastIndex);
} else {
// if there is already a ".." just add a ".."
normalized.add("..");
}
}
@Override
protected List<String> handleSingleDotDot(List<String> normalized) {
return normalized;
}
}