/* * Copyright 2000-2012 JetBrains s.r.o. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package com.intellij.openapi.vfs.impl; import com.intellij.openapi.Disposable; import com.intellij.openapi.application.Application; import com.intellij.openapi.application.ApplicationManager; import com.intellij.openapi.application.ModalityState; import com.intellij.openapi.diagnostic.Logger; import com.intellij.openapi.util.Disposer; import com.intellij.openapi.util.KeyedExtensionCollector; import com.intellij.openapi.vfs.*; import com.intellij.openapi.vfs.ex.VirtualFileManagerEx; import com.intellij.openapi.vfs.newvfs.BulkFileListener; import com.intellij.openapi.vfs.newvfs.CachingVirtualFileSystem; import com.intellij.openapi.vfs.newvfs.events.VFilePropertyChangeEvent; import com.intellij.util.EventDispatcher; import com.intellij.util.containers.ContainerUtil; import com.intellij.util.messages.MessageBus; import org.jetbrains.annotations.NotNull; import org.jetbrains.annotations.Nullable; import java.util.ArrayList; import java.util.Collections; import java.util.List; public class VirtualFileManagerImpl extends VirtualFileManagerEx { private static final Logger LOG = Logger.getInstance("#com.intellij.openapi.vfs.impl.VirtualFileManagerImpl"); private final KeyedExtensionCollector<VirtualFileSystem, String> myCollector = new KeyedExtensionCollector<VirtualFileSystem, String>("com.intellij.virtualFileSystem") { @Override protected String keyToString(String key) { return key; } }; private final List<VirtualFileSystem> myPhysicalFileSystems = new ArrayList<VirtualFileSystem>(); private final EventDispatcher<VirtualFileListener> myVirtualFileListenerMulticaster = EventDispatcher.create(VirtualFileListener.class); private final List<VirtualFileManagerListener> myVirtualFileManagerListeners = ContainerUtil.createLockFreeCopyOnWriteList(); private int myRefreshCount = 0; public VirtualFileManagerImpl(@NotNull VirtualFileSystem[] fileSystems, @NotNull MessageBus bus) { for (VirtualFileSystem fileSystem : fileSystems) { registerFileSystem(fileSystem); } if (LOG.isDebugEnabled()) { addVirtualFileListener(new LoggingListener()); } bus.connect().subscribe(VFS_CHANGES, new BulkVirtualFileListenerAdapter(myVirtualFileListenerMulticaster.getMulticaster())); } private void registerFileSystem(@NotNull VirtualFileSystem fileSystem) { myCollector.addExplicitExtension(fileSystem.getProtocol(), fileSystem); if (!(fileSystem instanceof CachingVirtualFileSystem)) { fileSystem.addVirtualFileListener(myVirtualFileListenerMulticaster.getMulticaster()); } myPhysicalFileSystems.add(fileSystem); } @Override @Nullable public VirtualFileSystem getFileSystem(@Nullable String protocol) { if (protocol == null) return null; List<VirtualFileSystem> systems = myCollector.forKey(protocol); if (systems.isEmpty()) return null; LOG.assertTrue(systems.size() == 1); return systems.get(0); } @Override public long syncRefresh() { return doRefresh(false, null); } @Override public long asyncRefresh(@Nullable Runnable postAction) { return doRefresh(true, postAction); } @Override public void refresh(boolean asynchronous) { doRefresh(asynchronous, null); } @Override public void refresh(boolean asynchronous, @Nullable final Runnable postAction) { doRefresh(asynchronous, postAction); } protected long doRefresh(boolean asynchronous, @Nullable Runnable postAction) { if (!asynchronous) { ApplicationManager.getApplication().assertIsDispatchThread(); } for (VirtualFileSystem fileSystem : getPhysicalFileSystems()) { if (!(fileSystem instanceof CachingVirtualFileSystem)) { fileSystem.refresh(asynchronous); } } return 0; } @Override public void refreshWithoutFileWatcher(final boolean asynchronous) { if (!asynchronous) { ApplicationManager.getApplication().assertIsDispatchThread(); } for (VirtualFileSystem fileSystem : getPhysicalFileSystems()) { if (fileSystem instanceof CachingVirtualFileSystem) { ((CachingVirtualFileSystem)fileSystem).refreshWithoutFileWatcher(asynchronous); } else { fileSystem.refresh(asynchronous); } } } private List<VirtualFileSystem> getPhysicalFileSystems() { return myPhysicalFileSystems; } @Override public VirtualFile findFileByUrl(@NotNull String url) { VirtualFileSystem fileSystem = getFileSystemForUrl(url); if (fileSystem == null) return null; return fileSystem.findFileByPath(extractPath(url)); } @Override public VirtualFile refreshAndFindFileByUrl(@NotNull String url) { VirtualFileSystem fileSystem = getFileSystemForUrl(url); if (fileSystem == null) return null; return fileSystem.refreshAndFindFileByPath(extractPath(url)); } @Nullable private VirtualFileSystem getFileSystemForUrl(String url) { String protocol = extractProtocol(url); if (protocol == null) return null; return getFileSystem(protocol); } @Override public void addVirtualFileListener(@NotNull VirtualFileListener listener) { myVirtualFileListenerMulticaster.addListener(listener); } @Override public void addVirtualFileListener(@NotNull VirtualFileListener listener, @NotNull Disposable parentDisposable) { myVirtualFileListenerMulticaster.addListener(listener, parentDisposable); } @Override public void removeVirtualFileListener(@NotNull VirtualFileListener listener) { myVirtualFileListenerMulticaster.removeListener(listener); } @Override public void addVirtualFileManagerListener(@NotNull VirtualFileManagerListener listener) { myVirtualFileManagerListeners.add(listener); } @Override public void addVirtualFileManagerListener(@NotNull final VirtualFileManagerListener listener, @NotNull Disposable parentDisposable) { addVirtualFileManagerListener(listener); Disposer.register(parentDisposable, new Disposable() { @Override public void dispose() { removeVirtualFileManagerListener(listener); } }); } @Override public void removeVirtualFileManagerListener(@NotNull VirtualFileManagerListener listener) { myVirtualFileManagerListeners.remove(listener); } @Override public void notifyPropertyChanged(final VirtualFile virtualFile, final String property, final Object oldValue, final Object newValue) { final Application application = ApplicationManager.getApplication(); final Runnable runnable = new Runnable() { @Override public void run() { if (virtualFile.isValid() && !application.isDisposed()) { application.runWriteAction(new Runnable() { @Override public void run() { List<VFilePropertyChangeEvent> events = Collections .singletonList(new VFilePropertyChangeEvent(this, virtualFile, property, oldValue, newValue, false)); BulkFileListener listener = application.getMessageBus().syncPublisher(VirtualFileManager.VFS_CHANGES); listener.before(events); listener.after(events); } }); } } }; application.invokeLater(runnable, ModalityState.NON_MODAL); } @Override public void fireBeforeRefreshStart(boolean asynchronous) { if (myRefreshCount++ == 0) { for (final VirtualFileManagerListener listener : myVirtualFileManagerListeners) { try { listener.beforeRefreshStart(asynchronous); } catch (Exception e) { LOG.error(e); } } } } @Override public void fireAfterRefreshFinish(boolean asynchronous) { if (--myRefreshCount == 0) { for (final VirtualFileManagerListener listener : myVirtualFileManagerListeners) { try { listener.afterRefreshFinish(asynchronous); } catch (Exception e) { LOG.error(e); } } } } @Override public long getModificationCount() { return 0; } @Override public List<LocalFileProvider> getLocalFileProviders(){ return ContainerUtil.findAll(myPhysicalFileSystems, LocalFileProvider.class); } private static class LoggingListener implements VirtualFileListener { @Override public void propertyChanged(VirtualFilePropertyEvent event) { LOG.debug("propertyChanged: file = " + event.getFile() + ", propertyName = " + event.getPropertyName() + ", oldValue = " + event.getOldValue() + ", newValue = " + event.getNewValue() + ", requestor = " + event.getRequestor()); } @Override public void contentsChanged(VirtualFileEvent event) { LOG.debug("contentsChanged: file = " + event.getFile() + ", requestor = " + event.getRequestor()); } @Override public void fileCreated(VirtualFileEvent event) { LOG.debug("fileCreated: file = " + event.getFile() + ", requestor = " + event.getRequestor()); } @Override public void fileDeleted(VirtualFileEvent event) { LOG.debug("fileDeleted: file = " + event.getFile() + ", parent = " + event.getParent() + ", requestor = " + event.getRequestor()); } @Override public void fileMoved(VirtualFileMoveEvent event) { LOG.debug("fileMoved: file = " + event.getFile() + ", oldParent = " + event.getOldParent() + ", newParent = " + event.getNewParent() + ", requestor = " + event.getRequestor()); } @Override public void fileCopied(VirtualFileCopyEvent event) { LOG.debug("fileCopied: file = " + event.getFile() + "originalFile = " + event.getOriginalFile() + ", requestor = " + event.getRequestor()); } @Override public void beforeContentsChange(VirtualFileEvent event) { LOG.debug("beforeContentsChange: file = " + event.getFile() + ", requestor = " + event.getRequestor()); } @Override public void beforePropertyChange(VirtualFilePropertyEvent event) { LOG.debug("beforePropertyChange: file = " + event.getFile() + ", propertyName = " + event.getPropertyName() + ", oldValue = " + event.getOldValue() + ", newValue = " + event.getNewValue() + ", requestor = " + event.getRequestor()); } @Override public void beforeFileDeletion(VirtualFileEvent event) { LOG.debug("beforeFileDeletion: file = " + event.getFile() + ", requestor = " + event.getRequestor()); LOG.assertTrue(event.getFile().isValid()); } @Override public void beforeFileMovement(VirtualFileMoveEvent event) { LOG.debug("beforeFileMovement: file = " + event.getFile() + ", oldParent = " + event.getOldParent() + ", newParent = " + event.getNewParent() + ", requestor = " + event.getRequestor()); } } }