All Downloads are FREE. Search and download functionalities are using the official Maven repository.

org.cryptomator.cryptofs.CryptoPath Maven / Gradle / Ivy

There is a newer version: 2.7.1-beta1
Show newest version
/*******************************************************************************
 * Copyright (c) 2016 Sebastian Stenzel and others.
 * All rights reserved. This program and the accompanying materials
 * are made available under the terms of the accompanying LICENSE.txt.
 *
 * Contributors:
 *     Sebastian Stenzel - initial API and implementation
 *******************************************************************************/
package org.cryptomator.cryptofs;

import org.cryptomator.cryptofs.common.ArrayUtils;

import java.io.File;
import java.io.IOException;
import java.net.URI;
import java.nio.file.LinkOption;
import java.nio.file.Path;
import java.nio.file.ProviderMismatchException;
import java.nio.file.WatchEvent;
import java.nio.file.WatchKey;
import java.nio.file.WatchService;
import java.util.ArrayList;
import java.util.Collections;
import java.util.Iterator;
import java.util.LinkedList;
import java.util.List;
import java.util.NoSuchElementException;
import java.util.Objects;

import static org.cryptomator.cryptofs.common.Constants.SEPARATOR;

public class CryptoPath implements Path {

	private static final String CURRENT_DIR = ".";
	private static final String PARENT_DIR = "..";

	private final CryptoFileSystemImpl fileSystem;
	private final Symlinks symlinks;
	private final List elements;
	private final boolean absolute;

	CryptoPath(CryptoFileSystemImpl fileSystem, Symlinks symlinks, List elements, boolean absolute) {
		fileSystem.assertOpen();
		this.fileSystem = Objects.requireNonNull(fileSystem);
		this.symlinks = Objects.requireNonNull(symlinks);
		this.elements = Collections.unmodifiableList(elements);
		this.absolute = Objects.requireNonNull(absolute);
	}

	static CryptoPath castAndAssertAbsolute(Path path) {
		CryptoPath result = cast(path);
		if (!result.isAbsolute()) {
			throw new IllegalArgumentException("Path must be absolute but was " + path);
		}
		return result;
	}

	static CryptoPath cast(Path path) {
		if (path instanceof CryptoPath p) {
			p.getFileSystem().assertOpen();
			return p;
		} else {
			throw new ProviderMismatchException("Used a path from different provider: " + path);
		}
	}

	@Override
	public CryptoFileSystemImpl getFileSystem() {
		return fileSystem;
	}

	// visible for testing
	List getElements() {
		return elements;
	}

	@Override
	public boolean isAbsolute() {
		fileSystem.assertOpen();
		return absolute;
	}

	@Override
	public CryptoPath getRoot() {
		fileSystem.assertOpen();
		return absolute ? fileSystem.getRootPath() : null;
	}

	@Override
	public CryptoPath getFileName() {
		fileSystem.assertOpen();
		int elementCount = getNameCount();
		if (elementCount == 0) {
			return null;
		} else {
			return getName(elementCount - 1);
		}
	}

	@Override
	public CryptoPath getParent() {
		fileSystem.assertOpen();
		int elementCount = getNameCount();
		if (elementCount > 1) {
			List elems = elements.subList(0, elementCount - 1);
			return copyWithElements(elems);
		} else if (elementCount == 1) {
			return getRoot();
		} else {
			return null; // only root and the "empty" path don't have a parent
		}
	}

	@Override
	public int getNameCount() {
		fileSystem.assertOpen();
		return elements.size();
	}

	@Override
	public CryptoPath getName(int index) {
		fileSystem.assertOpen();
		return subpath(index, index + 1);
	}

	@Override
	public CryptoPath subpath(int beginIndex, int endIndex) {
		fileSystem.assertOpen();
		final List sublist;
		try {
			sublist = elements.subList(beginIndex, endIndex);
		} catch (IndexOutOfBoundsException e) {
			throw new IllegalArgumentException(e);
		}
		return new CryptoPath(fileSystem, symlinks, sublist, false);
	}

	@Override
	public boolean startsWith(Path path) {
		fileSystem.assertOpen();
		if (!this.getFileSystem().equals(path.getFileSystem())) {
			return false;
		}
		CryptoPath other = cast(path);
		boolean matchesAbsolute = this.isAbsolute() == other.isAbsolute();
		if (matchesAbsolute && other.elements.size() <= this.elements.size()) {
			return this.elements.subList(0, other.elements.size()).equals(other.elements);
		} else {
			return false;
		}
	}

	@Override
	public boolean startsWith(String other) {
		fileSystem.assertOpen();
		return startsWith(fileSystem.getPath(other));
	}

	@Override
	public boolean endsWith(Path path) {
		fileSystem.assertOpen();
		if (!this.getFileSystem().equals(path.getFileSystem())) {
			return false;
		}
		CryptoPath other = cast(path);
		if (other.elements.size() <= this.elements.size()) {
			return this.elements.subList(this.elements.size() - other.elements.size(), this.elements.size()).equals(other.elements);
		} else {
			return false;
		}
	}

	@Override
	public boolean endsWith(String other) {
		fileSystem.assertOpen();
		return endsWith(fileSystem.getPath(other));
	}

	@Override
	public CryptoPath normalize() {
		fileSystem.assertOpen();
		LinkedList normalized = new LinkedList<>();
		for (String elem : elements) {
			String lastElem = normalized.peekLast();
			if (elem.isEmpty() || CURRENT_DIR.equals(elem)) {
				continue;
			} else if (PARENT_DIR.equals(elem) && lastElem != null && !PARENT_DIR.equals(lastElem)) {
				normalized.removeLast();
			} else {
				normalized.add(elem);
			}
		}
		return copyWithElements(normalized);
	}

	@Override
	public CryptoPath resolve(Path path) {
		fileSystem.assertOpen();
		CryptoPath other = cast(path);
		if (other.isAbsolute()) {
			return other;
		} else {
			List joined = new ArrayList<>();
			joined.addAll(this.elements);
			joined.addAll(other.elements);
			return copyWithElements(joined);
		}
	}

	@Override
	public CryptoPath resolve(String other) {
		fileSystem.assertOpen();
		return resolve(fileSystem.getPath(other));
	}

	@Override
	public CryptoPath resolveSibling(Path path) {
		fileSystem.assertOpen();
		CryptoPath parent = getParent();
		CryptoPath other = cast(path);
		if (parent == null || other.isAbsolute()) {
			return other;
		} else {
			return parent.resolve(other);
		}
	}

	@Override
	public CryptoPath resolveSibling(String other) {
		fileSystem.assertOpen();
		return resolveSibling(fileSystem.getPath(other));
	}

	@Override
	public CryptoPath relativize(Path path) {
		fileSystem.assertOpen();
		CryptoPath normalized = this.normalize();
		CryptoPath other = cast(path).normalize();
		if (normalized.isAbsolute() == other.isAbsolute()) {
			int commonPrefix = countCommonPrefixElements(normalized, other);
			int stepsUp = this.getNameCount() - commonPrefix;
			List elems = new ArrayList<>();
			elems.addAll(Collections.nCopies(stepsUp, PARENT_DIR));
			elems.addAll(other.elements.subList(commonPrefix, other.getNameCount()));
			return copyWithElementsAndAbsolute(elems, false);
		} else {
			throw new IllegalArgumentException("Can't relativize an absolute path relative to a relative path.");
		}
	}

	private int countCommonPrefixElements(CryptoPath p1, CryptoPath p2) {
		int n = Math.min(p1.getNameCount(), p2.getNameCount());
		for (int i = 0; i < n; i++) {
			if (!p1.elements.get(i).equals(p2.elements.get(i))) {
				return i;
			}
		}
		return n;
	}

	@Override
	public URI toUri() {
		fileSystem.assertOpen();
		return CryptoFileSystemUri.create(fileSystem.getPathToVault(), elements.toArray(new String[elements.size()]));
	}

	@Override
	public CryptoPath toAbsolutePath() {
		fileSystem.assertOpen();
		if (isAbsolute()) {
			return this;
		} else {
			return copyWithAbsolute(true);
		}
	}

	@Override
	public CryptoPath toRealPath(LinkOption... options) throws IOException {
		fileSystem.assertOpen();
		CryptoPath normalized = normalize().toAbsolutePath();
		if (!ArrayUtils.contains(options, LinkOption.NOFOLLOW_LINKS)) {
			return normalized.resolveAllSymlinksInPath();
		} else {
			return normalized;
		}
	}

	private CryptoPath resolveAllSymlinksInPath() throws IOException {
		if (getNameCount() > 1) {
			CryptoPath p = getParent().resolveAllSymlinksInPath().resolve(getFileName());
			return symlinks.resolveRecursively(p).normalize();
		} else {
			return symlinks.resolveRecursively(this).normalize();
		}
	}

	@Override
	public File toFile() {
		fileSystem.assertOpen();
		throw new UnsupportedOperationException();
	}

	@Override
	public WatchKey register(WatchService watcher, WatchEvent.Kind[] events, WatchEvent.Modifier... modifiers) throws IOException {
		fileSystem.assertOpen();
		throw new UnsupportedOperationException("Method not implemented.");
	}

	@Override
	public WatchKey register(WatchService watcher, WatchEvent.Kind... events) throws IOException {
		fileSystem.assertOpen();
		throw new UnsupportedOperationException("Method not implemented.");
	}

	@Override
	public Iterator iterator() {
		fileSystem.assertOpen();
		return new Iterator<>() {

			private int idx = 0;

			@Override
			public boolean hasNext() {
				return idx < getNameCount();
			}

			@Override
			public Path next() {
				try {
					return getName(idx++);
				} catch (IllegalArgumentException e) {
					throw new NoSuchElementException(e);
				}
			}
		};
	}

	@Override
	public int compareTo(Path path) {
		CryptoPath other = (CryptoPath) path;
		if (this.isAbsolute() != other.isAbsolute()) {
			return this.isAbsolute() ? -1 : 1;
		}
		for (int i = 0; i < Math.min(this.getNameCount(), other.getNameCount()); i++) {
			int result = this.elements.get(i).compareTo(other.elements.get(i));
			if (result != 0) {
				return result;
			}
		}
		return this.getNameCount() - other.getNameCount();
	}

	@Override
	public int hashCode() {
		int hash = 0;
		hash = 31 * hash + fileSystem.hashCode();
		hash = 31 * hash + elements.hashCode();
		hash = 31 * hash + (absolute ? 1 : 0);
		return hash;
	}

	@Override
	public boolean equals(Object obj) {
		if (obj instanceof CryptoPath other) {
			return this.fileSystem.equals(other.fileSystem) //
					&& this.compareTo(other) == 0;
		} else {
			return false;
		}
	}

	@Override
	public String toString() {
		String prefix = absolute ? SEPARATOR : "";
		return prefix + String.join(SEPARATOR, elements);
	}

	private CryptoPath copyWithElements(List elements) {
		return new CryptoPath(fileSystem, symlinks, elements, absolute);
	}

	private CryptoPath copyWithAbsolute(boolean absolute) {
		return new CryptoPath(fileSystem, symlinks, elements, absolute);
	}

	private CryptoPath copyWithElementsAndAbsolute(List elements, boolean absolute) {
		return new CryptoPath(fileSystem, symlinks, elements, absolute);
	}

}




© 2015 - 2024 Weber Informatics LLC | Privacy Policy