X-Git-Url: https://git.argeo.org/?a=blobdiff_plain;f=org.argeo.api%2Fsrc%2Forg%2Fargeo%2Fapi%2Fgcr%2Ffs%2FAbstractFsPath.java;fp=org.argeo.api%2Fsrc%2Forg%2Fargeo%2Fapi%2Fgcr%2Ffs%2FAbstractFsPath.java;h=c83fe385b6d2696c0ed3cb8f487cc947b7ce51b9;hb=8767858f1fca02ea7c53ca244f6c1fa745d91ac4;hp=0000000000000000000000000000000000000000;hpb=5a36795f16b1b2a58188db84d6546b501626bda8;p=lgpl%2Fargeo-commons.git diff --git a/org.argeo.api/src/org/argeo/api/gcr/fs/AbstractFsPath.java b/org.argeo.api/src/org/argeo/api/gcr/fs/AbstractFsPath.java new file mode 100644 index 000000000..c83fe385b --- /dev/null +++ b/org.argeo.api/src/org/argeo/api/gcr/fs/AbstractFsPath.java @@ -0,0 +1,387 @@ +package org.argeo.api.gcr.fs; + +import java.io.File; +import java.io.IOException; +import java.net.URI; +import java.net.URISyntaxException; +import java.nio.file.LinkOption; +import java.nio.file.Path; +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.Arrays; +import java.util.Iterator; +import java.util.NoSuchElementException; + +public abstract class AbstractFsPath, ST extends AbstractFsStore> implements Path { + private final FS fs; + /** null for non absolute paths */ + private final ST fileStore; + + private final String[] segments;// null means root + private final boolean absolute; + + private final String separator; + + // optim + private final int hashCode; + + public AbstractFsPath(FS filesSystem, String path) { + if (path == null) + throw new IllegalArgumentException("Path cannot be null"); + this.fs = filesSystem; + this.separator = fs.getSeparator(); + // TODO deal with both path and separator being empty strings + if (path.equals(separator)) {// root + this.segments = null; + this.absolute = true; + this.hashCode = 0; + this.fileStore = fs.getBaseFileStore(); + return; + } else if (path.equals("")) {// empty path + this.segments = new String[] { "" }; + this.absolute = false; + this.hashCode = "".hashCode(); + this.fileStore = null; + return; + } + + this.absolute = path.startsWith(toStringRoot()); + + String trimmedPath = path.substring(absolute ? toStringRoot().length() : 0, + path.endsWith(separator) ? path.length() - separator.length() : path.length()); + this.segments = trimmedPath.split(separator); + // clean up + for (int i = 0; i < this.segments.length; i++) { + this.segments[i] = cleanUpSegment(this.segments[i]); + } + this.hashCode = this.segments[this.segments.length - 1].hashCode(); + + this.fileStore = isAbsolute() ? fs.getFileStore(path) : null; + } + + protected AbstractFsPath(FS filesSystem, ST fileStore, String[] segments, boolean absolute) { + this.segments = segments; + this.absolute = absolute; + this.hashCode = segments == null ? 0 : segments[segments.length - 1].hashCode(); + this.separator = filesSystem.getSeparator(); +// super(path, path == null ? true : absolute, filesSystem.getSeparator()); +// assert path == null ? absolute == true : true; + this.fs = filesSystem; +// this.path = path; +// this.absolute = path == null ? true : absolute; + if (isAbsolute() && fileStore == null) + throw new IllegalArgumentException("Absolute path requires a file store"); + if (!isAbsolute() && fileStore != null) + throw new IllegalArgumentException("A file store should not be provided for a relative path"); + this.fileStore = fileStore; + assert !(absolute && fileStore == null); + } + + protected Path retrieve(String path) { + return getFileSystem().getPath(path); + } + + @Override + public FS getFileSystem() { + return fs; + } + + public ST getFileStore() { + return fileStore; + } + + @Override + public boolean isAbsolute() { + return absolute; + } + + @Override + public URI toUri() { + try { + return new URI(fs.provider().getScheme(), toString(), null); + } catch (URISyntaxException e) { + throw new IllegalStateException("Cannot create URI for " + toString(), e); + } + } + + @Override + public Path toAbsolutePath() { + if (isAbsolute()) + return this; + // FIXME it doesn't seem right + return newInstance(getSegments(), true); + } + + @Override + public Path toRealPath(LinkOption... options) throws IOException { + return this; + } + + @Override + public File toFile() { + throw new UnsupportedOperationException(); + } + + /* + * PATH OPERATIONS + */ + public final Path resolveSibling(Path other) { + if (other == null) + throw new NullPointerException(); + Path parent = getParent(); + return (parent == null) ? other : parent.resolve(other); + } + + @Override + public final Path resolveSibling(String other) { + return resolveSibling(getFileSystem().getPath(other)); + } + + public final Path resolve(String other) { + return resolve(retrieve(other)); + } + + public boolean startsWith(Path other) { + return toString().startsWith(other.toString()); + } + + public boolean endsWith(Path other) { + return toString().endsWith(other.toString()); + } + + @Override + public Path normalize() { + // always normalized + return this; + } + + @Override + public final Iterator iterator() { + return new Iterator() { + private int i = 0; + + @Override + public boolean hasNext() { + return (i < getNameCount()); + } + + @Override + public Path next() { + if (i < getNameCount()) { + Path result = getName(i); + i++; + return result; + } else { + throw new NoSuchElementException(); + } + } + + @Override + public void remove() { + throw new UnsupportedOperationException(); + } + }; + } + + @Override + public int compareTo(Path other) { + return toString().compareTo(other.toString()); + } + + public Path resolve(Path other) { + AbstractFsPath otherPath = (AbstractFsPath) other; + if (otherPath.isAbsolute()) + return other; + String[] newPath; + if (isRoot()) { + newPath = new String[otherPath.segments.length]; + System.arraycopy(otherPath.segments, 0, newPath, 0, otherPath.segments.length); + } else { + newPath = new String[segments.length + otherPath.segments.length]; + System.arraycopy(segments, 0, newPath, 0, segments.length); + System.arraycopy(otherPath.segments, 0, newPath, segments.length, otherPath.segments.length); + } + if (!absolute) + return newInstance(newPath, absolute); + else { + return newInstance(toString(newPath)); + } + } + + public Path relativize(Path other) { + if (equals(other)) + return newInstance(""); + if (other.toString().startsWith(this.toString())) { + String p1 = toString(); + String p2 = other.toString(); + String relative = p2.substring(p1.length(), p2.length()); + if (relative.charAt(0) == '/') + relative = relative.substring(1); + return newInstance(relative); + } + throw new IllegalArgumentException(other + " cannot be relativized against " + this); + } + + /* + * FACTORIES + */ + protected abstract AbstractFsPath newInstance(String path); + + protected abstract AbstractFsPath newInstance(String[] segments, boolean absolute); + + /* + * CUSTOMISATIONS + */ + protected String toStringRoot() { + return separator; + } + + protected String cleanUpSegment(String segment) { + return segment; + } + + protected boolean isRoot() { + return segments == null; + } + + protected boolean isEmpty() { + return segments.length == 1 && "".equals(segments[0]); + } + + /* + * PATH OPERATIONS + */ + public AbstractFsPath getRoot() { + return newInstance(toStringRoot()); + } + + public AbstractFsPath getParent() { + if (isRoot()) + return null; + // FIXME empty path? + if (segments.length == 1)// first level + return newInstance(toStringRoot()); + String[] parentPath = Arrays.copyOfRange(segments, 0, segments.length - 1); + if (!absolute) + return newInstance(parentPath, absolute); + else + return newInstance(toString(parentPath)); + } + + public AbstractFsPath getFileName() { + if (isRoot()) + return null; + return newInstance(segments[segments.length - 1]); + } + + public int getNameCount() { + if (isRoot()) + return 0; + return segments.length; + } + + public AbstractFsPath getName(int index) { + if (isRoot()) + return null; + return newInstance(segments[index]); + } + + public AbstractFsPath subpath(int beginIndex, int endIndex) { + if (isRoot()) + return null; + String[] parentPath = Arrays.copyOfRange(segments, beginIndex, endIndex); + return newInstance(parentPath, false); + } + + public boolean startsWith(String other) { + return toString().startsWith(other); + } + + public boolean endsWith(String other) { + return toString().endsWith(other); + } + + /* + * UTILITIES + */ + protected String toString(String[] path) { + if (isRoot()) + return toStringRoot(); + StringBuilder sb = new StringBuilder(); + if (isAbsolute()) + sb.append(separator); + for (int i = 0; i < path.length; i++) { + if (i != 0) + sb.append(separator); + sb.append(path[i]); + } + return sb.toString(); + } + + @Override + public String toString() { + return toString(segments); + } + + @Override + public int hashCode() { + return hashCode; + } + + @Override + public boolean equals(Object obj) { + if (!(obj instanceof AbstractFsPath)) + return false; + AbstractFsPath other = (AbstractFsPath) obj; + + if (isRoot()) {// root + if (other.isRoot())// root + return true; + else + return false; + } else { + if (other.isRoot())// root + return false; + } + // non root + if (segments.length != other.segments.length) + return false; + for (int i = 0; i < segments.length; i++) { + if (!segments[i].equals(other.segments[i])) + return false; + } + return true; + } + + @Override + protected Object clone() throws CloneNotSupportedException { + return newInstance(toString()); + } + + /* + * GETTERS / SETTERS + */ + protected String[] getSegments() { + return segments; + } + + protected String getSeparator() { + return separator; + } + + /* + * UNSUPPORTED + */ + @Override + public WatchKey register(WatchService watcher, Kind[] events, Modifier... modifiers) throws IOException { + throw new UnsupportedOperationException(); + } + + @Override + public WatchKey register(WatchService watcher, Kind... events) throws IOException { + throw new UnsupportedOperationException(); + } + +}