private void applyChildrenChangeEvents(VirtualFile parent, List<VFileEvent> events) { final NewVirtualFileSystem delegate = getDelegate(parent); TIntArrayList childrenIdsUpdated = new TIntArrayList(); List<VirtualFile> childrenToBeUpdated = new SmartList<VirtualFile>(); assert parent != null && parent != mySuperRoot; final int parentId = getFileId(parent); assert parentId != 0; TIntHashSet parentChildrenIds = new TIntHashSet(FSRecords.list(parentId)); boolean hasRemovedChildren = false; for (VFileEvent event : events) { if (event instanceof VFileCreateEvent) { String name = ((VFileCreateEvent) event).getChildName(); final VirtualFile fake = new FakeVirtualFile(parent, name); final FileAttributes attributes = delegate.getAttributes(fake); if (attributes != null) { final int childId = createAndFillRecord(delegate, fake, parentId, attributes); assert parent instanceof VirtualDirectoryImpl : parent; final VirtualDirectoryImpl dir = (VirtualDirectoryImpl) parent; VirtualFileSystemEntry child = dir.createChild(name, childId, dir.getFileSystem()); childrenToBeUpdated.add(child); childrenIdsUpdated.add(childId); parentChildrenIds.add(childId); } } else if (event instanceof VFileDeleteEvent) { VirtualFile file = ((VFileDeleteEvent) event).getFile(); if (!file.exists()) { LOG.error("Deleting a file, which does not exist: " + file.getPath()); continue; } hasRemovedChildren = true; int id = getFileId(file); childrenToBeUpdated.add(file); childrenIdsUpdated.add(-id); parentChildrenIds.remove(id); } } FSRecords.updateList(parentId, parentChildrenIds.toArray()); if (hasRemovedChildren) clearIdCache(); VirtualDirectoryImpl parentImpl = (VirtualDirectoryImpl) parent; for (int i = 0, len = childrenIdsUpdated.size(); i < len; ++i) { final int childId = childrenIdsUpdated.get(i); final VirtualFile childFile = childrenToBeUpdated.get(i); if (childId > 0) { parentImpl.addChild((VirtualFileSystemEntry) childFile); } else { FSRecords.deleteRecordRecursively(-childId); parentImpl.removeChild(childFile); invalidateSubtree(childFile); } } }
@NotNull private static VirtualFilePointer[] toPointers(@NotNull List<FilePointerPartNode> pointers) { if (pointers.isEmpty()) return VirtualFilePointer.EMPTY_ARRAY; List<VirtualFilePointer> list = ContainerUtil.mapNotNull( pointers, new Function<FilePointerPartNode, VirtualFilePointer>() { @Override public VirtualFilePointer fun(FilePointerPartNode pair) { return pair.leaf; } }); return list.toArray(new VirtualFilePointer[list.size()]); }
@RequiredWriteAction @Override public void processEvents(@NotNull List<VFileEvent> events) { ApplicationManager.getApplication().assertWriteAccessAllowed(); List<VFileEvent> validated = validateEvents(events); BulkFileListener publisher = myEventBus.syncPublisher(VirtualFileManager.VFS_CHANGES); publisher.before(validated); THashMap<VirtualFile, List<VFileEvent>> parentToChildrenEventsChanges = null; for (VFileEvent event : validated) { VirtualFile changedParent = null; if (event instanceof VFileCreateEvent) { changedParent = ((VFileCreateEvent) event).getParent(); ((VFileCreateEvent) event).resetCache(); } else if (event instanceof VFileDeleteEvent) { changedParent = ((VFileDeleteEvent) event).getFile().getParent(); } if (changedParent != null) { if (parentToChildrenEventsChanges == null) parentToChildrenEventsChanges = new THashMap<VirtualFile, List<VFileEvent>>(); List<VFileEvent> parentChildrenChanges = parentToChildrenEventsChanges.get(changedParent); if (parentChildrenChanges == null) { parentToChildrenEventsChanges.put( changedParent, parentChildrenChanges = new SmartList<VFileEvent>()); } parentChildrenChanges.add(event); } else { applyEvent(event); } } if (parentToChildrenEventsChanges != null) { parentToChildrenEventsChanges.forEachEntry( new TObjectObjectProcedure<VirtualFile, List<VFileEvent>>() { @Override public boolean execute(VirtualFile parent, List<VFileEvent> childrenEvents) { applyChildrenChangeEvents(parent, childrenEvents); return true; } }); parentToChildrenEventsChanges.clear(); } publisher.after(validated); }
private List<VFileEvent> getEvents(String msg, @Nullable Runnable action) { LOG.debug("** waiting for " + msg); myAccept = true; if (action != null) { action.run(); } int timeout = myTimeout; try { synchronized (myWaiter) { //noinspection WaitNotInLoop myWaiter.wait(timeout); } } catch (InterruptedException e) { LOG.warn(e); } LOG.debug("** waited for " + timeout); myFileSystem.refresh(false); ArrayList<VFileEvent> result; synchronized (myEvents) { result = new ArrayList<VFileEvent>(myEvents); myEvents.clear(); } LOG.debug("** events: " + result.size()); return result; }
private void scheduleWritableAttributeChange( final VirtualFileSystemEntry file, final boolean currentWritable, final boolean uptodateWritable) { myEvents.add( new VFilePropertyChangeEvent( null, file, VirtualFile.PROP_WRITABLE, currentWritable, uptodateWritable, true)); }
@Override @NotNull public VirtualFile[] getLocalRoots() { List<VirtualFile> roots = ContainerUtil.newSmartList(); myRootsLock.readLock().lock(); try { for (NewVirtualFile root : myRoots.values()) { if (root.isInLocalFileSystem() && !(root.getFileSystem() instanceof TempFileSystem)) { roots.add(root); } } } finally { myRootsLock.readLock().unlock(); } return VfsUtilCore.toVirtualFileArray(roots); }
@Override @NotNull public VirtualFile[] getRoots(@NotNull final NewVirtualFileSystem fs) { final List<VirtualFile> roots = new ArrayList<VirtualFile>(); myRootsLock.readLock().lock(); try { for (NewVirtualFile root : myRoots.values()) { if (root.getFileSystem() == fs) { roots.add(root); } } } finally { myRootsLock.readLock().unlock(); } return VfsUtilCore.toVirtualFileArray(roots); }
private void assertEvent(Class<? extends VFileEvent> type, String... paths) { List<VFileEvent> events = getEvents(type.getSimpleName(), null); assertEquals(events.toString(), paths.length, events.size()); Set<String> pathSet = ContainerUtil.map2Set( paths, new Function<String, String>() { @Override public String fun(final String path) { return FileUtil.toSystemIndependentName(path); } }); for (VFileEvent event : events) { assertTrue(event.toString(), type.isInstance(event)); VirtualFile eventFile = event.getFile(); assertNotNull(event.toString(), eventFile); assertTrue( eventFile + " not in " + Arrays.toString(paths), pathSet.remove(eventFile.getPath())); } }
@NotNull private FSRecords.NameId[] persistAllChildren( @NotNull final VirtualFile file, final int id, @NotNull FSRecords.NameId[] current) { assert file != mySuperRoot; final NewVirtualFileSystem fs = replaceWithNativeFS(getDelegate(file)); String[] delegateNames = VfsUtil.filterNames(fs.list(file)); if (delegateNames.length == 0 && current.length > 0) { return current; } Set<String> toAdd = ContainerUtil.newHashSet(delegateNames); for (FSRecords.NameId nameId : current) { toAdd.remove(nameId.name); } final TIntArrayList childrenIds = new TIntArrayList(current.length + toAdd.size()); final List<FSRecords.NameId> nameIds = ContainerUtil.newArrayListWithExpectedSize(current.length + toAdd.size()); for (FSRecords.NameId nameId : current) { childrenIds.add(nameId.id); nameIds.add(nameId); } for (String newName : toAdd) { FakeVirtualFile child = new FakeVirtualFile(file, newName); FileAttributes attributes = fs.getAttributes(child); if (attributes != null) { int childId = createAndFillRecord(fs, child, id, attributes); childrenIds.add(childId); nameIds.add(new FSRecords.NameId(childId, FileNameCache.storeName(newName), newName)); } } FSRecords.updateList(id, childrenIds.toNativeArray()); setChildrenCached(id); return nameIds.toArray(new FSRecords.NameId[nameIds.size()]); }
@Override public void after(@NotNull final List<? extends VFileEvent> events) { cleanContainerCaches(); if (myUrlsToUpdate == null) { return; } for (String url : myUrlsToUpdate) { synchronized (VirtualFilePointerManagerImpl.this) { for (TreeMap<String, VirtualFilePointerImpl> urlToPointer : myUrlToPointerMaps.values()) { VirtualFilePointerImpl pointer = urlToPointer.remove(url); if (pointer != null) { String path = VfsUtil.urlToPath(pointer.getUrl()); urlToPointer.put(path, pointer); } } } } for (VirtualFilePointer pointer : myPointersToUpdate) { ((VirtualFilePointerImpl) pointer).update(); } for (EventDescriptor event : myEvents) { event.fireAfter(); } if (!myPointersToUpdate.isEmpty()) { VirtualFilePointer[] arr = myPointersToUpdate.toArray(new VirtualFilePointer[myPointersToUpdate.size()]); myBus.syncPublisher(VirtualFilePointerListener.TOPIC).validityChanged(arr); } myUrlsToUpdate = null; myEvents = null; myPointersToUpdate = null; }
private void addPointersUnder( String path, boolean allowSameFSOptimization, List<VirtualFilePointer> pointers) { final boolean urlFromJarFS = allowSameFSOptimization && path.indexOf(JarFileSystem.JAR_SEPARATOR) > 0; for (TreeMap<String, VirtualFilePointerImpl> urlToPointer : myUrlToPointerMaps.values()) { for (String pointerUrl : urlToPointer.keySet()) { final boolean pointerFromJarFS = allowSameFSOptimization && pointerUrl.indexOf(JarFileSystem.JAR_SEPARATOR) > 0; if (urlFromJarFS != pointerFromJarFS) { continue; // optimization: consider pointers from the same FS as the url specified } if (startsWith(path, pointerUrl)) { VirtualFilePointer pointer = urlToPointer.get(pointerUrl); if (pointer != null) { pointers.add(pointer); } } } } }
@NotNull private static List<VFileEvent> validateEvents(@NotNull List<VFileEvent> events) { final List<EventWrapper> deletionEvents = ContainerUtil.newArrayList(); for (int i = 0, size = events.size(); i < size; i++) { final VFileEvent event = events.get(i); if (event instanceof VFileDeleteEvent && event.isValid()) { deletionEvents.add(new EventWrapper((VFileDeleteEvent) event, i)); } } final TIntHashSet invalidIDs; if (deletionEvents.isEmpty()) { invalidIDs = EmptyIntHashSet.INSTANCE; } else { ContainerUtil.quickSort(deletionEvents, DEPTH_COMPARATOR); invalidIDs = new TIntHashSet(deletionEvents.size()); final Set<VirtualFile> dirsToBeDeleted = new THashSet<VirtualFile>(deletionEvents.size()); nextEvent: for (EventWrapper wrapper : deletionEvents) { final VirtualFile candidate = wrapper.event.getFile(); VirtualFile parent = candidate; while (parent != null) { if (dirsToBeDeleted.contains(parent)) { invalidIDs.add(wrapper.id); continue nextEvent; } parent = parent.getParent(); } if (candidate.isDirectory()) { dirsToBeDeleted.add(candidate); } } } final List<VFileEvent> filtered = new ArrayList<VFileEvent>(events.size() - invalidIDs.size()); for (int i = 0, size = events.size(); i < size; i++) { final VFileEvent event = events.get(i); if (event.isValid() && !(event instanceof VFileDeleteEvent && invalidIDs.contains(i))) { filtered.add(event); } } return filtered; }
@Override public void before(@NotNull final List<? extends VFileEvent> events) { cleanContainerCaches(); List<VirtualFilePointer> toFireEvents = new ArrayList<VirtualFilePointer>(); List<String> toUpdateUrl = new ArrayList<String>(); synchronized (VirtualFilePointerManagerImpl.this) { for (VFileEvent event : events) { if (event instanceof VFileDeleteEvent) { final VFileDeleteEvent deleteEvent = (VFileDeleteEvent) event; String url = deleteEvent.getFile().getPath(); addPointersUnder(url, true, toFireEvents); } else if (event instanceof VFileCreateEvent) { final VFileCreateEvent createEvent = (VFileCreateEvent) event; String url = createEvent.getPath(); addPointersUnder(url, false, toFireEvents); } else if (event instanceof VFileCopyEvent) { final VFileCopyEvent copyEvent = (VFileCopyEvent) event; String url = copyEvent.getNewParent().getPath() + "/" + copyEvent.getFile().getName(); addPointersUnder(url, false, toFireEvents); } else if (event instanceof VFileMoveEvent) { final VFileMoveEvent moveEvent = (VFileMoveEvent) event; List<VirtualFilePointer> pointers = new ArrayList<VirtualFilePointer>(); addPointersUnder(moveEvent.getFile().getPath(), false, pointers); for (VirtualFilePointer pointer : pointers) { VirtualFile file = pointer.getFile(); if (file != null) { toUpdateUrl.add(file.getPath()); } } } else if (event instanceof VFilePropertyChangeEvent) { final VFilePropertyChangeEvent change = (VFilePropertyChangeEvent) event; if (VirtualFile.PROP_NAME.equals(change.getPropertyName())) { List<VirtualFilePointer> pointers = new ArrayList<VirtualFilePointer>(); addPointersUnder(change.getFile().getPath(), false, pointers); for (VirtualFilePointer pointer : pointers) { VirtualFile file = pointer.getFile(); if (file != null) { toUpdateUrl.add(file.getPath()); } } } } } myEvents = new ArrayList<EventDescriptor>(); for (VirtualFilePointerListener listener : myUrlToPointerMaps.keySet()) { if (listener == null) continue; EventDescriptor event = new EventDescriptor(listener, toFireEvents); myEvents.add(event); } } for (EventDescriptor event : myEvents) { event.fireBefore(); } if (!toFireEvents.isEmpty()) { VirtualFilePointer[] arr = toFireEvents.toArray(new VirtualFilePointer[toFireEvents.size()]); myBus.syncPublisher(VirtualFilePointerListener.TOPIC).beforeValidityChanged(arr); } myPointersToUpdate = toFireEvents; myUrlsToUpdate = toUpdateUrl; }
@Override public void before(@NotNull final List<? extends VFileEvent> events) { List<FilePointerPartNode> toFireEvents = new ArrayList<FilePointerPartNode>(); List<FilePointerPartNode> toUpdateUrl = new ArrayList<FilePointerPartNode>(); VirtualFilePointer[] toFirePointers; synchronized (this) { incModificationCount(); for (VFileEvent event : events) { if (event instanceof VFileDeleteEvent) { final VFileDeleteEvent deleteEvent = (VFileDeleteEvent) event; addPointersUnder(deleteEvent.getFile(), false, "", toFireEvents); } else if (event instanceof VFileCreateEvent) { final VFileCreateEvent createEvent = (VFileCreateEvent) event; addPointersUnder(createEvent.getParent(), true, createEvent.getChildName(), toFireEvents); } else if (event instanceof VFileCopyEvent) { final VFileCopyEvent copyEvent = (VFileCopyEvent) event; addPointersUnder( copyEvent.getNewParent(), true, copyEvent.getFile().getName(), toFireEvents); } else if (event instanceof VFileMoveEvent) { final VFileMoveEvent moveEvent = (VFileMoveEvent) event; VirtualFile eventFile = moveEvent.getFile(); addPointersUnder(moveEvent.getNewParent(), true, eventFile.getName(), toFireEvents); List<FilePointerPartNode> nodes = new ArrayList<FilePointerPartNode>(); addPointersUnder(eventFile, false, "", nodes); for (FilePointerPartNode pair : nodes) { VirtualFile file = pair.leaf.getFile(); if (file != null) { toUpdateUrl.add(pair); } } } else if (event instanceof VFilePropertyChangeEvent) { final VFilePropertyChangeEvent change = (VFilePropertyChangeEvent) event; if (VirtualFile.PROP_NAME.equals(change.getPropertyName())) { VirtualFile eventFile = change.getFile(); VirtualFile parent = eventFile.getParent(); // e.g. for LightVirtualFiles addPointersUnder(parent, true, change.getNewValue().toString(), toFireEvents); List<FilePointerPartNode> nodes = new ArrayList<FilePointerPartNode>(); addPointersUnder(eventFile, false, "", nodes); for (FilePointerPartNode pair : nodes) { VirtualFile file = pair.leaf.getFile(); if (file != null) { toUpdateUrl.add(pair); } } } } } myEvents = new ArrayList<EventDescriptor>(); toFirePointers = toPointers(toFireEvents); for (final VirtualFilePointerListener listener : myPointers.keySet()) { if (listener == null) continue; List<VirtualFilePointer> filtered = ContainerUtil.filter( toFirePointers, new Condition<VirtualFilePointer>() { @Override public boolean value(VirtualFilePointer pointer) { return ((VirtualFilePointerImpl) pointer).getListener() == listener; } }); if (!filtered.isEmpty()) { EventDescriptor event = new EventDescriptor( listener, filtered.toArray(new VirtualFilePointer[filtered.size()])); myEvents.add(event); } } } for (EventDescriptor descriptor : myEvents) { descriptor.fireBefore(); } if (!toFireEvents.isEmpty()) { myBus.syncPublisher(VirtualFilePointerListener.TOPIC).beforeValidityChanged(toFirePointers); } myPointersToFire = toFireEvents; myPointersToUpdateUrl = toUpdateUrl; }
private void scheduleUpdateContent(final VirtualFileSystemEntry file) { myEvents.add(new VFileContentChangeEvent(null, file, file.getModificationStamp(), -1, true)); }
private void scheduleCreation( final VirtualFileSystemEntry parent, final String childName, final boolean isDirectory) { myEvents.add(new VFileCreateEvent(null, parent, childName, isDirectory, true)); }
private void scheduleDeletion(final VirtualFile file) { if (file == null) return; myEvents.add(new VFileDeleteEvent(null, file, true)); }