package peergos.shared.user;
import peergos.shared.*;
import peergos.shared.user.fs.*;
import peergos.shared.util.*;
import java.util.*;
import java.util.concurrent.*;
import java.util.stream.*;
public class TrieNode {
private final Map<String, TrieNode> children;
private final Optional<EntryPoint> value;
private final Map<String, String> pathMappings;
public TrieNode(Map<String, TrieNode> children, Optional<EntryPoint> value, Map<String, String> pathMappings) {
this.children = Collections.unmodifiableMap(children);
this.value = value;
this.pathMappings = Collections.unmodifiableMap(pathMappings);
}
public TrieNode() {
this(Collections.emptyMap(), Optional.empty(), Collections.emptyMap());
}
public CompletableFuture<Optional<FileTreeNode>> getByPath(String path, NetworkAccess network) {
System.out.println("GetByPath: " + path);
for (String prefix: pathMappings.keySet()) {
if (path.startsWith(prefix)) {
path = pathMappings.get(prefix) + path.substring(prefix.length());
}
}
String finalPath = path.startsWith("/") ? path.substring(1) : path;
if (finalPath.length() == 0) {
if (! value.isPresent()) { // find a child entry and traverse parent links
return children.values().stream()
.findAny()
.get()
.getByPath("", network)
.thenCompose(child -> child.get()
.retrieveParent(network)
.thenApply(opt -> opt.map(f -> f.withTrieNode(this))));
}
return network.retrieveEntryPoint(value.get());
}
String[] elements = finalPath.split("/");
// There may be an entry point further down the tree, but it will have <= permission than this one
if (value.isPresent())
return network.retrieveEntryPoint(value.get())
.thenCompose(dir -> dir.get().getDescendentByPath(finalPath, network));
if (!children.containsKey(elements[0]))
return CompletableFuture.completedFuture(Optional.empty());
return children.get(elements[0]).getByPath(finalPath.substring(elements[0].length()), network);
}
public CompletableFuture<Set<FileTreeNode>> getChildren(String path, NetworkAccess network) {
String trimmedPath = path.startsWith("/") ? path.substring(1) : path;
if (trimmedPath.length() == 0) {
if (!value.isPresent()) { // find a child entry and traverse parent links
Set<CompletableFuture<Optional<FileTreeNode>>> kids = children.values().stream()
.map(t -> t.getByPath("", network)).collect(Collectors.toSet());
return Futures.combineAll(kids)
.thenApply(set -> set.stream()
.filter(opt -> opt.isPresent())
.map(opt -> opt.get())
.collect(Collectors.toSet()));
}
return network.retrieveEntryPoint(value.get())
.thenCompose(dir -> dir.get().getChildren(network));
}
String[] elements = trimmedPath.split("/");
if (!children.containsKey(elements[0]))
return network.retrieveEntryPoint(value.get())
.thenCompose(dir -> dir.get().getDescendentByPath(trimmedPath, network)
.thenCompose(parent -> parent.get().getChildren(network)));
return children.get(elements[0]).getChildren(trimmedPath.substring(elements[0].length()), network);
}
public Set<String> getChildNames() {
return children.keySet();
}
public TrieNode put(String path, EntryPoint e) {
System.out.println("Entrie.put(" + path + ")");
if (path.startsWith("/"))
path = path.substring(1);
if (path.length() == 0) {
return new TrieNode(children, Optional.of(e), pathMappings);
}
String[] elements = path.split("/");
TrieNode existing = children.getOrDefault(elements[0], new TrieNode());
TrieNode newChild = existing.put(path.substring(elements[0].length()), e);
HashMap<String, TrieNode> newChildren = new HashMap<>(children);
newChildren.put(elements[0], newChild);
return new TrieNode(newChildren, value, pathMappings);
}
public TrieNode removeEntry(String path) {
System.out.println("Entrie.rm(" + path + ")");
for (String prefix: pathMappings.keySet()) {
if (path.startsWith(prefix)) {
path = pathMappings.get(prefix) + path.substring(prefix.length());
}
}
if (path.startsWith("/"))
path = path.substring(1);
if (path.length() == 0) {
return new TrieNode(children, Optional.empty(), pathMappings);
}
String[] elements = path.split("/");
TrieNode existing = children.getOrDefault(elements[0], new TrieNode());
TrieNode newChild = existing.removeEntry(path.substring(elements[0].length()));
HashMap<String, TrieNode> newChildren = new HashMap<>(children);
if (newChild.isEmpty())
newChildren.remove(elements[0]);
else
newChildren.put(elements[0], newChild);
return new TrieNode(newChildren, value, pathMappings);
}
public TrieNode addPathMapping(String prefix, String target) {
Map<String, String> newLinks = new HashMap<>(pathMappings);
newLinks.put(prefix, target);
return new TrieNode(children, value, newLinks);
}
public boolean isEmpty() {
return children.size() == 0 && !value.isPresent();
}
public TrieNode clear() {
return new TrieNode();
}
}