package jetbrains.mps.ide.platform.watching;
/*Generated by MPS */
import java.util.concurrent.locks.ReadWriteLock;
import java.util.concurrent.locks.ReentrantReadWriteLock;
import java.util.concurrent.ConcurrentMap;
import jetbrains.mps.vfs.FileSystemListener;
import java.util.concurrent.ConcurrentHashMap;
import java.util.regex.Pattern;
import org.jetbrains.annotations.NotNull;
import java.util.List;
import java.util.ArrayList;
public class FileSystemListenersContainer {
private final ReadWriteLock myLock = new ReentrantReadWriteLock();
private final FileSystemListenersContainer.Node root = new FileSystemListenersContainer.Node(null, null);
private final ConcurrentMap<FileSystemListener, String> myListeners = new ConcurrentHashMap<FileSystemListener, String>();
private final Pattern myPathSplitPattern;
public FileSystemListenersContainer() {
myPathSplitPattern = Pattern.compile("/");
}
public void addListener(@NotNull FileSystemListener listener) {
if (myListeners.containsKey(listener)) {
return;
}
if (listener.getFileToListen() == null) {
return;
}
String path = listener.getFileToListen().getPath();
if (myListeners.putIfAbsent(listener, path) != null) {
return;
}
FileSystemListenersContainer.Node curr = root;
myLock.writeLock().lock();
try {
for (String s : normalizeAndSplit(path)) {
if ((s == null || s.length() == 0)) {
continue;
}
curr = curr.child(s, true);
}
curr.addListener(listener);
} finally {
myLock.writeLock().unlock();
}
}
public void removeListener(@NotNull FileSystemListener listener) {
String path = myListeners.get(listener);
if (path == null) {
return;
}
FileSystemListenersContainer.Node curr = root;
myLock.writeLock().lock();
try {
for (String s : normalizeAndSplit(path)) {
if ((s == null || s.length() == 0)) {
continue;
}
curr = curr.child(s, false);
if (curr == null) {
return;
}
}
curr.removeListener(listener);
} finally {
myLock.writeLock().unlock();
}
myListeners.remove(listener);
}
public Iterable<FileSystemListener> listeners(@NotNull String path) {
FileSystemListenersContainer.Node curr = root;
List<FileSystemListener> result = new ArrayList<FileSystemListener>();
myLock.readLock().lock();
try {
root.storeListeners(result);
for (String s : normalizeAndSplit(path)) {
if ((s == null || s.length() == 0)) {
continue;
}
curr = curr.child(s, false);
if (curr == null) {
return result;
}
curr.storeListeners(result);
}
} finally {
myLock.readLock().unlock();
}
return result;
}
private String[] normalizeAndSplit(@NotNull String path) {
String normalized = path.replace('\\', '/');
return myPathSplitPattern.split(normalized, 0);
}
public boolean contains(@NotNull FileSystemListener listener) {
return myListeners.containsKey(listener);
}
private static class Node {
private List<FileSystemListener> listeners;
private final String pathPart;
private final List<FileSystemListenersContainer.Node> children = new ArrayList<FileSystemListenersContainer.Node>(4);
private final FileSystemListenersContainer.Node parent;
/*package*/ Node(String pathPart, FileSystemListenersContainer.Node parent) {
this.parent = parent;
this.pathPart = pathPart;
}
/*package*/ FileSystemListenersContainer.Node child(String part, boolean create) {
// we keep children list sorted and use binary search
int index = childIndex(part);
if (index >= 0) {
return children.get(index);
}
if (create) {
FileSystemListenersContainer.Node child = new FileSystemListenersContainer.Node(part, this);
children.add(-index - 1, child);
return child;
}
return null;
}
private void deleteIfEmpty() {
if (parent == null || !(children.isEmpty())) {
return;
}
if (listeners != null && !(listeners.isEmpty())) {
return;
}
listeners = null;
parent.children.remove(this);
parent.deleteIfEmpty();
}
private int childIndex(String pathPart) {
int low = 0;
int high = children.size() - 1;
while (low <= high) {
int mid = (low + high) >>> 1;
FileSystemListenersContainer.Node c = children.get(mid);
int cmp = pathPart.compareTo(c.pathPart);
if (cmp < 0) {
high = mid - 1;
} else if (cmp > 0) {
low = mid + 1;
} else {
return mid;
}
}
return -(low + 1);
}
/*package*/ void storeListeners(List<FileSystemListener> result) {
if (listeners != null) {
result.addAll(listeners);
}
}
/*package*/ void addListener(FileSystemListener l) {
if (listeners == null) {
listeners = new ArrayList<FileSystemListener>(4);
}
listeners.add(l);
}
/*package*/ void removeListener(FileSystemListener l) {
if (listeners != null && listeners.remove(l)) {
deleteIfEmpty();
}
}
}
}