/* * Copyright 2013-2014 Odysseus Software GmbH * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package org.musicmount.io.server; import java.io.File; import java.io.IOException; import java.net.URI; import java.net.URISyntaxException; import java.nio.file.FileSystem; import java.nio.file.LinkOption; import java.nio.file.Path; import java.nio.file.WatchEvent; import java.nio.file.WatchKey; import java.nio.file.WatchService; import java.util.ArrayList; import java.util.Arrays; import java.util.Iterator; import java.util.List; public class ServerPath implements Path { private static <T> int findCommenPrefix(List<T> one, List<T> two) { int n = Math.min(one.size(), two.size()); for (int i = 0; i < n; i++) { if (!one.get(i).equals(two.get(i))) { return i; } } return n; } private static boolean isDirectoryElement(String element) { return ".".equals(element) || "..".equals(element); } private final ServerFileSystem fileSystem; private final List<String> elems; private final boolean absolute; private final boolean directory; public ServerPath(ServerFileSystem fileSystem, String first, String... more) { this.fileSystem = fileSystem; this.absolute = first.startsWith(fileSystem.getSeparator()); this.elems = new ArrayList<>(); for (String sub : first.split(fileSystem.getSeparator())) { if (!sub.isEmpty()) { elems.add(sub); } } for (String next : more) { for (String sub : next.split(fileSystem.getSeparator())) { if (!sub.isEmpty()) { elems.add(sub); } } } if (elems.size() > 0 && isDirectoryElement(elems.get(elems.size() - 1))) { this.directory = true; } else { String last = more.length > 0 ? more[more.length - 1] : first; this.directory = last.endsWith(fileSystem.getSeparator()); } } private ServerPath(ServerFileSystem fileSystem, boolean absolute, boolean directory, String... elems) { this(fileSystem, absolute, directory, Arrays.asList(elems)); } private ServerPath(ServerFileSystem fileSystem, boolean absolute, boolean directory, List<String> elems) { this.fileSystem = fileSystem; this.elems = elems; this.absolute = absolute; this.directory = directory; } public boolean isDirectory() { return directory; } @Override public FileSystem getFileSystem() { return fileSystem; } @Override public boolean isAbsolute() { return absolute; } @Override public Path getRoot() { return absolute ? new ServerPath(fileSystem, true, true, fileSystem.getSeparator()) : null; } @Override public Path getFileName() { if (elems.isEmpty()) { return null; } if (elems.size() == 1) { return absolute ? new ServerPath(fileSystem, false, directory, elems) : this; } return new ServerPath(fileSystem, false, directory, elems.get(elems.size() - 1)); } @Override public ServerPath getParent() { if (elems.isEmpty() || !absolute && elems.size() == 1) { return null; } return new ServerPath(fileSystem, absolute, true, elems.subList(0, elems.size() - 1)); } @Override public int getNameCount() { return elems.size(); } @Override public Path getName(int index) { return new ServerPath(fileSystem, false, false, elems.get(index)); } @Override public Path subpath(int beginIndex, int endIndex) { return new ServerPath(fileSystem, absolute && beginIndex == 0, directory, elems.subList(beginIndex, endIndex)); } @Override public boolean startsWith(Path other) { if (absolute != other.isAbsolute()) { return false; } if (elems.size() < other.getNameCount()) { return false; } for (int i = 0; i < other.getNameCount(); i++) { if (!elems.get(i).equals(other.getName(i).toString())) { return false; } } return true; } @Override public boolean startsWith(String other) { return startsWith(getFileSystem().getPath(other)); } @Override public boolean endsWith(Path other) { if (other.isAbsolute()) { if (!absolute || elems.size() > other.getNameCount()) { return false; } } if (elems.size() < other.getNameCount()) { return false; } for (int i = 0; i < other.getNameCount(); i++) { if (!elems.get(elems.size() - 1 - i).equals(other.getName(other.getNameCount() - 1 - i).toString())) { return false; } } return true; } @Override public boolean endsWith(String other) { return endsWith(getFileSystem().getPath(other)); } @Override public Path normalize() { if (!elems.contains(".") && !elems.contains("..")) { return this; } List<String> normalizedElems = new ArrayList<>(); for (String elem : elems) { switch (elem) { case "..": if (normalizedElems.size() > 0 && !normalizedElems.get(normalizedElems.size() - 1).equals("..")) { normalizedElems.remove(normalizedElems.size() - 1); } else { normalizedElems.add(elem); } break; case ".": break; default: normalizedElems.add(elem); } } return new ServerPath(fileSystem, absolute, directory, normalizedElems); } @Override public Path resolve(Path other) { if (other.isAbsolute()) { return other; } if (other.getNameCount() == 0) { return this; } ServerPath path = (ServerPath) other; List<String> resolvedElems = new ArrayList<>(elems); resolvedElems.addAll(path.elems); return new ServerPath(fileSystem, absolute, path.directory, resolvedElems); } @Override public ServerPath resolve(String other) { return (ServerPath) resolve(new ServerPath(fileSystem, other)); } @Override public Path resolveSibling(Path other) { if (other.isAbsolute()) { return other; } Path parent = getParent(); if (parent == null) { return other; } return getParent().resolve(other); } @Override public Path resolveSibling(String other) { return resolveSibling(new ServerPath(fileSystem, other)); } @Override public Path relativize(Path other) { if (absolute != other.isAbsolute()) { throw new IllegalArgumentException("cannot relativize between absolute and relative path"); } if (elems.size() == 0 && !other.isAbsolute()) { return other; } if (other.startsWith(this)) { return other.subpath(elems.size(), other.getNameCount()); } List<String> from = elems; List<String> to = ((ServerPath) other).elems; List<String> path = new ArrayList<>(); int prefix = findCommenPrefix(from, to); for (int i = prefix; i < from.size(); i++) { path.add(".."); } for (int i = prefix; i < to.size(); i++) { path.add(to.get(i)); } return new ServerPath(fileSystem, false, ((ServerPath) other).directory, path); } @Override public URI toUri() { try { return fileSystem.getServerUri(this); } catch (URISyntaxException e) { throw new RuntimeException(e); } } @Override public Path toAbsolutePath() { return absolute ? this : fileSystem.getBaseDirectory().resolve(this); } @Override public Path toRealPath(LinkOption... options) throws IOException { return toAbsolutePath().normalize(); } ServerPath toDirectoryPath(boolean directory) { return directory == this.directory ? this : new ServerPath(fileSystem, absolute, directory, elems); } @Override public File toFile() { throw new UnsupportedOperationException(); } @Override public WatchKey register(WatchService watcher, WatchEvent.Kind<?>[] events, WatchEvent.Modifier... modifiers) throws IOException { throw new UnsupportedOperationException(); } @Override public WatchKey register(WatchService watcher, WatchEvent.Kind<?>... events) throws IOException { throw new UnsupportedOperationException(); } @Override public Iterator<Path> iterator() { return new Iterator<Path>() { private int index = 0; @Override public boolean hasNext() { return index < elems.size(); } @Override public Path next() { return getName(index++); } @Override public void remove() { throw new UnsupportedOperationException(); } }; } @Override public int compareTo(Path other) { int minCount = Math.min(getNameCount(), other.getNameCount()); for (int i = 0; i < minCount; i++) { int result = getName(i).toString().compareTo(other.getName(i).toString()); if (result != 0) { return result; } } return Integer.valueOf(getNameCount()).compareTo(Integer.valueOf(other.getNameCount())); } @Override public String toString() { StringBuilder builder = new StringBuilder(); if (absolute) { builder.append(fileSystem.getSeparator()); if (elems.isEmpty()) { // root return builder.toString(); } } for (int i = 0; i < elems.size(); i++) { if (i > 0) { builder.append(fileSystem.getSeparator()); } builder.append(elems.get(i)); } if (directory && (elems.size() == 0 || !isDirectoryElement(elems.get(elems.size() - 1)))) { // do not append '/' to '.' or '..' builder.append(fileSystem.getSeparator()); } return builder.toString(); } @Override public int hashCode() { final int prime = 31; int result = 1; result = prime * result + (absolute ? 1231 : 1237); result = prime * result + (directory ? 1231 : 1237); result = prime * result + ((elems == null) ? 0 : elems.hashCode()); result = prime * result + ((fileSystem == null) ? 0 : fileSystem.hashCode()); return result; } @Override public boolean equals(Object obj) { if (this == obj) { return true; } if (obj == null) { return false; } if (getClass() != obj.getClass()) { return false; } ServerPath other = (ServerPath) obj; if (absolute != other.absolute) { return false; } if (directory != other.directory) { return false; } if (elems == null) { if (other.elems != null) { return false; } } else if (!elems.equals(other.elems)) { return false; } if (fileSystem == null) { if (other.fileSystem != null) { return false; } } else if (!fileSystem.equals(other.fileSystem)) { return false; } return true; } }