package com.github.marschall.memoryfilesystem; import static com.github.marschall.memoryfilesystem.MemoryFileSystemProvider.SCHEME; import java.net.URI; import java.net.URISyntaxException; import java.nio.file.Path; import java.text.CollationKey; import java.text.Collator; import java.util.ArrayList; import java.util.Arrays; import java.util.Collections; import java.util.List; final class AbsolutePath extends NonEmptyPath { private final Root root; AbsolutePath(MemoryFileSystem fileSystem, Root root, List<String> nameElements) { super(fileSystem, nameElements); this.root = root; } @Override public boolean isAbsolute() { return true; } @Override public Path getRoot() { return this.root; } @Override public String toString() { // TODO estimate size StringBuilder buffer = new StringBuilder(); buffer.append(this.root); boolean first = true; String separator = this.getFileSystem().getSeparator(); for (String element : this.getNameElements()) { if (!first) { buffer.append(separator); } buffer.append(element); first = false; } return buffer.toString(); } @Override public Path getParent() { if (this.getNameElements().size() == 1) { return this.root; } else { List<String> subList = this.getNameElements().subList(0, this.getNameElements().size() - 1); return createAboslute(this.getMemoryFileSystem(), this.root, subList); } } @Override public Path getName(int index) { if (index < 0) { throw new IllegalArgumentException("index must be positive but was " + index); } if (index >= this.getNameCount()) { throw new IllegalArgumentException("index must not be bigger than " + (this.getNameCount() - 1) + " but was " + index); } 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 createRelative(this.getMemoryFileSystem(), this.getNameElements()); } else { return createRelative(this.getMemoryFileSystem(), this.getNameElements().subList(beginIndex, endIndex)); } } @Override protected List<String> handleSingleDotDot(List<String> normalized) { return Collections.emptyList(); } @Override protected void handleDotDotNormalizationAlreadyModified(List<String> normalized) { normalized.remove(normalized.size() - 1); } @Override protected List<String> handleDotDotNormalizationNotYetModified(List<String> nameElements, int nameElementsSize, int i) { // copy everything preceding the element before ".." List<String> normalized = new ArrayList<>(nameElementsSize - 1); if (i > 1) { normalized.addAll(nameElements.subList(0, i - 1)); } return normalized; } @Override Path newInstance(MemoryFileSystem fileSystem, List<String> pathElements) { return createAboslute(fileSystem, this.root, pathElements); } @Override public URI toUri() { String fileSystemKey = this.getMemoryFileSystem().getKey(); boolean isNamed = this.root.isNamed(); List<String> nameElements = this.getNameElements(); int sizeEsitmate = fileSystemKey.length() + 3 // "://".length + (isNamed ? 3 :0) // c:/ + nameElements.size() // n * '/' + totalLength(nameElements); StringBuilder schemeSpecificPart = new StringBuilder(sizeEsitmate); schemeSpecificPart.append(fileSystemKey); schemeSpecificPart.append("://"); if (isNamed) { schemeSpecificPart.append('/'); schemeSpecificPart.append(this.root.getKey()); schemeSpecificPart.append(':'); } for (String element : nameElements) { schemeSpecificPart.append('/'); schemeSpecificPart.append(element); } try { return new URI(SCHEME, schemeSpecificPart.toString(), null); } catch (URISyntaxException e) { throw new AssertionError("could not create URI"); } } private static int totalLength(List<String> elements) { int totalLength = 0; for (String each : elements) { totalLength += each.length(); } return totalLength; } @Override public Path toAbsolutePath() { return this; } @Override int compareToNonRoot(ElementPath other) { if (!other.isAbsolute()) { return -1; } return this.compareNameElements(other.getNameElements()); } @Override boolean startsWith(AbstractPath other) { if (!other.isAbsolute()) { return false; } if (other.isRoot()) { // is other my root? return other.equals(this.getRoot()); } // named roots under windows if (!this.getRoot().startsWith(other.getRoot())) { return false; } if (other instanceof ElementPath) { ElementPath otherPath = (ElementPath) other; int otherNameCount = otherPath.getNameCount(); if (otherNameCount > this.getNameCount()) { return false; } Collator collator = this.getMemoryFileSystem().getCollator(); // otherNameCount smaller or equal to this.getNameCount() 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 this.equals(other); } 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 createAboslute(this.getMemoryFileSystem(), this.root, 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 createAboslute(this.getMemoryFileSystem(), this.root, resolvedElements); } else { throw new IllegalArgumentException("can't resolve" + other); } } @Override Path relativize(AbstractPath other) { if (!other.isAbsolute()) { // only support relativization against absolute paths throw new IllegalArgumentException("can only relativize an absolute path against an absolute path"); } if (!other.getRoot().equals(this.root)) { // only support relativization against paths with same root throw new IllegalArgumentException("paths must have the same root"); } if (other.equals(this.root)) { // other is my root, easy return createRelative(this.getMemoryFileSystem(), ParentReferenceList.create(this.getNameCount())); } else 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 AbsolutePath)) { return false; } AbsolutePath other = (AbsolutePath) obj; // takes file system key into account return this.root.equals(other.root) && this.equalElementsAs(other.getNameElements()); } @Override public int hashCode() { // TODO expensive, safe Collator collator = this.getMemoryFileSystem().getCollator(); int result = 17; result = 31 * result + this.root.hashCode(); for (String each : this.getNameElements()) { CollationKey collationKey = collator.getCollationKey(each); result = 31 * result + Arrays.hashCode(collationKey.toByteArray()); } return result; } }