package jetbrains.mps.ide.platform.watching; /*Generated by MPS */ import org.apache.log4j.Logger; import org.apache.log4j.LogManager; import java.util.Map; import jetbrains.mps.vfs.FileSystemListener; import jetbrains.mps.internal.collections.runtime.MapSequence; import java.util.HashMap; import java.util.Queue; import jetbrains.mps.internal.collections.runtime.QueueSequence; import java.util.LinkedList; import jetbrains.mps.ide.vfs.IdeaFileSystem; import jetbrains.mps.vfs.FileSystemExtPoint; import org.jetbrains.mps.openapi.util.ProgressMonitor; import jetbrains.mps.internal.collections.runtime.Sequence; import org.jetbrains.mps.openapi.util.SubProgressKind; import java.util.Set; import java.util.LinkedHashSet; import jetbrains.mps.internal.collections.runtime.SetSequence; import com.intellij.openapi.vfs.VirtualFile; import jetbrains.mps.fileTypes.MPSFileTypesManager; import jetbrains.mps.vfs.IFile; import jetbrains.mps.ide.vfs.IdeaFile; import jetbrains.mps.internal.collections.runtime.IVisitor; import jetbrains.mps.internal.collections.runtime.ISelector; import jetbrains.mps.InternalFlag; import jetbrains.mps.vfs.FileSystemEvent; import java.util.HashSet; import org.jetbrains.annotations.NotNull; import java.util.Arrays; public class FileProcessor extends ReloadParticipant { private static final Logger LOG = LogManager.getLogger(FileProcessor.class); private FileSystemListenersContainer listenersContainer; private Map<FileSystemListener, FileProcessor.ListenerData> dataMap = MapSequence.fromMap(new HashMap<FileSystemListener, FileProcessor.ListenerData>()); private Queue<FileSystemListener> postNotify = QueueSequence.fromQueue(new LinkedList<FileSystemListener>()); private static final IdeaFileSystem FS = ((IdeaFileSystem) FileSystemExtPoint.getFS()); public FileProcessor() { listenersContainer = FS.getListenersContainer(); } @Override public void update(ProgressMonitor monitor) { if (MapSequence.fromMap(dataMap).isEmpty()) { return; } monitor.start("", MapSequence.fromMap(dataMap).count() + 1); long updateStartTime = System.currentTimeMillis(); try { for (FileSystemListener listener : Sequence.fromIterable(sortedListeners())) { FileProcessor.ListenerData data = MapSequence.fromMap(dataMap).get(listener); if (!(listenersContainer.contains(listener))) { monitor.advance(1); continue; } long listenerUpdateStartTime = System.currentTimeMillis(); listener.update(monitor.subTask(1, SubProgressKind.AS_COMMENT), data); printStat("update:" + listener, listenerUpdateStartTime); data.isNotified = true; } long postNotifyBeginTime = System.currentTimeMillis(); FileSystemListener listener; while ((listener = QueueSequence.fromQueue(postNotify).removeFirstElement()) != null) { FileProcessor.ListenerData data = MapSequence.fromMap(dataMap).get(listener); if (data.isNotified) { continue; } listener.update(monitor.subTask(0, SubProgressKind.AS_COMMENT), data); data.isNotified = true; } printStat("post-notify", postNotifyBeginTime); } finally { printStat("update", updateStartTime); monitor.done(); } } private void notify(FileSystemListener listener, FileProcessor.ListenerData source) { FileProcessor.ListenerData data = MapSequence.fromMap(dataMap).get(listener); if (data == null) { data = new FileProcessor.ListenerData(); MapSequence.fromMap(dataMap).put(listener, data); QueueSequence.fromQueue(postNotify).addLastElement(listener); } else if (data.isNotified) { return; } data.added.addAll(source.added); data.changed.addAll(source.changed); data.removed.addAll(source.removed); } private Iterable<FileSystemListener> sortedListeners() { Set<FileSystemListener> result = new LinkedHashSet<FileSystemListener>(MapSequence.fromMap(dataMap).count()); for (FileSystemListener l : SetSequence.fromSet(MapSequence.fromMap(dataMap).keySet())) { visit(l, result); } return result; } private void visit(FileSystemListener listener, Set<FileSystemListener> result) { if (result.contains(listener)) { return; } result.add(listener); Iterable<FileSystemListener> dependencies = listener.getListenerDependencies(); if (dependencies == null) { return; } boolean readd = false; for (FileSystemListener dep : dependencies) { if (MapSequence.fromMap(dataMap).containsKey(dep) && !(result.contains(dep))) { visit(dep, result); readd = true; } } if (readd) { result.remove(listener); result.add(listener); } } protected boolean accepts(VirtualFile file) { return !(MPSFileTypesManager.isFileIgnored(file.getPath())); } protected void processDelete(VirtualFile file) { final IFile ifile = new IdeaFile(FS, file); Sequence.fromIterable(get(ifile.getPath())).visitAll(new IVisitor<FileProcessor.ListenerData>() { public void visit(FileProcessor.ListenerData it) { it.removed.add(ifile); } }); } protected void processCreate(VirtualFile file) { String path = file.getPath(); final IFile ifile = FS.getFile(path); Sequence.fromIterable(get(path)).visitAll(new IVisitor<FileProcessor.ListenerData>() { public void visit(FileProcessor.ListenerData it) { it.added.add(ifile); } }); } protected void processContentChanged(VirtualFile file) { String path = file.getPath(); final IFile ifile = FS.getFile(path); Sequence.fromIterable(get(path)).visitAll(new IVisitor<FileProcessor.ListenerData>() { public void visit(FileProcessor.ListenerData it) { it.changed.add(ifile); } }); } @Override public boolean isEmpty() { return MapSequence.fromMap(dataMap).isEmpty(); } public Iterable<FileProcessor.ListenerData> get(String path) { return Sequence.fromIterable(listenersContainer.listeners(path)).select(new ISelector<FileSystemListener, FileProcessor.ListenerData>() { public FileProcessor.ListenerData select(FileSystemListener it) { FileProcessor.ListenerData data = MapSequence.fromMap(dataMap).get(it); if (data == null) { data = new FileProcessor.ListenerData(); MapSequence.fromMap(dataMap).put(it, data); } return data; } }); } private void printStat(String name, long beginTime) { // todo: ideal for AOP in MPS! if (InternalFlag.isInternalMode()) { if (LOG.isDebugEnabled()) { LOG.debug("FileProcessor: " + name + " -> " + (System.currentTimeMillis() - beginTime) / 1000.0 + "s"); } } } private class ListenerData implements FileSystemEvent { private Set<IFile> added = new HashSet<IFile>(); private Set<IFile> removed = new HashSet<IFile>(); private Set<IFile> changed = new HashSet<IFile>(); private boolean isNotified; private ListenerData() { } @Override public Set<IFile> getCreated() { return added; } @Override public Set<IFile> getRemoved() { return removed; } @Override public Set<IFile> getChanged() { return changed; } @Override public void notify(FileSystemListener listener) { FileProcessor.this.notify(listener, this); } @Override public String toString() { return String.format("[added: %s; removed: %s; changed: %s.", setToString(added), setToString(removed), setToString(changed)); } @NotNull private String setToString(Set<?> set) { return Arrays.toString(set.toArray()); } } }