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(); } } } }