| /* |
| * Copyright 2000-2014 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.cvsSupport2.application; |
| |
| import com.intellij.cvsSupport2.CvsUtil; |
| import com.intellij.cvsSupport2.config.CvsApplicationLevelConfiguration; |
| import com.intellij.cvsSupport2.config.CvsRootConfiguration; |
| import com.intellij.cvsSupport2.connections.CvsConnectionSettings; |
| import com.intellij.cvsSupport2.connections.IDEARootFormatter; |
| import com.intellij.cvsSupport2.cvsIgnore.IgnoredFilesInfo; |
| import com.intellij.cvsSupport2.cvsIgnore.UserDirIgnores; |
| import com.intellij.cvsSupport2.cvsstatuses.CvsEntriesListener; |
| import com.intellij.cvsSupport2.util.CvsVfsUtil; |
| import com.intellij.openapi.application.ApplicationManager; |
| import com.intellij.openapi.components.ServiceManager; |
| import com.intellij.openapi.diagnostic.Logger; |
| import com.intellij.openapi.progress.ProcessCanceledException; |
| import com.intellij.openapi.project.Project; |
| import com.intellij.openapi.project.ProjectManager; |
| import com.intellij.openapi.vcs.FileStatusManager; |
| import com.intellij.openapi.vcs.changes.VcsDirtyScopeManager; |
| import com.intellij.openapi.vfs.*; |
| import com.intellij.util.ArrayUtil; |
| import com.intellij.util.containers.ContainerUtil; |
| import gnu.trove.THashMap; |
| import gnu.trove.THashSet; |
| import org.jetbrains.annotations.NotNull; |
| import org.jetbrains.annotations.Nullable; |
| import org.netbeans.lib.cvsclient.admin.Entry; |
| |
| import java.io.File; |
| import java.util.Collection; |
| import java.util.Map; |
| |
| /** |
| * author: lesya |
| */ |
| |
| public class CvsEntriesManager extends VirtualFileAdapter { |
| |
| private static final Logger LOG = Logger.getInstance("#com.intellij.cvsSupport2.application.CvsEntriesManager"); |
| |
| private final Map<VirtualFile, CvsInfo> myInfoByParentDirectoryPath = new THashMap<VirtualFile, CvsInfo>(); |
| |
| private static final String CVS_ADMIN_DIRECTORY_NAME = CvsUtil.CVS; |
| |
| private final Collection<CvsEntriesListener> myEntriesListeners = ContainerUtil.createLockFreeCopyOnWriteList(); |
| private int myIsActive = 0; |
| private final Collection<String> myFilesToRefresh = new THashSet<String>(); |
| |
| private final Map<String, CvsConnectionSettings> myStringToSettingsMap = new THashMap<String, CvsConnectionSettings>(); |
| private final UserDirIgnores myUserDirIgnores = new UserDirIgnores(); |
| private final MyVirtualFileManagerListener myVirtualFileManagerListener = new MyVirtualFileManagerListener(); |
| private final CvsApplicationLevelConfiguration myApplicationLevelConfiguration; |
| |
| public static CvsEntriesManager getInstance() { |
| return ServiceManager.getService(CvsEntriesManager.class); |
| } |
| |
| public CvsEntriesManager(final CvsApplicationLevelConfiguration applicationLevelConfiguration) { |
| myApplicationLevelConfiguration = applicationLevelConfiguration; |
| } |
| |
| private class MyVirtualFileManagerListener implements VirtualFileManagerListener { |
| public void afterRefreshFinish(boolean asynchonous) { |
| ensureFilesCached(); //to cache for next refreshes |
| } |
| |
| public void beforeRefreshStart(boolean asynchonous) { |
| } |
| } |
| |
| public void registerAsVirtualFileListener() { |
| if (myIsActive == 0) { |
| VirtualFileManager.getInstance().addVirtualFileListener(this); |
| VirtualFileManager.getInstance().addVirtualFileManagerListener(myVirtualFileManagerListener); |
| } |
| myIsActive++; |
| } |
| |
| public synchronized void unregisterAsVirtualFileListener() { |
| LOG.assertTrue(isActive()); |
| myIsActive--; |
| if (myIsActive == 0) { |
| VirtualFileManager.getInstance().removeVirtualFileListener(this); |
| VirtualFileManager.getInstance().removeVirtualFileManagerListener(myVirtualFileManagerListener); |
| myInfoByParentDirectoryPath.clear(); |
| } |
| } |
| |
| public void beforePropertyChange(@NotNull VirtualFilePropertyEvent event) { |
| processEvent(event); |
| } |
| |
| public void beforeContentsChange(@NotNull VirtualFileEvent event) { |
| processEvent(event); |
| } |
| |
| public void contentsChanged(@NotNull VirtualFileEvent event) { |
| fireStatusChanged(event.getFile()); |
| } |
| |
| |
| @NotNull |
| private synchronized CvsInfo getInfoFor(VirtualFile parent) { |
| if (parent == null) return CvsInfo.getDummyCvsInfo(); |
| if (!myInfoByParentDirectoryPath.containsKey(parent)) { |
| CvsInfo cvsInfo = new CvsInfo(parent); |
| myInfoByParentDirectoryPath.put(cvsInfo.getKey(), cvsInfo); |
| } |
| return myInfoByParentDirectoryPath.get(parent); |
| } |
| |
| public synchronized void clearCachedFiltersFor(final VirtualFile parent) { |
| for (final VirtualFile file : myInfoByParentDirectoryPath.keySet()) { |
| if (file == null) continue; |
| if (!file.isValid()) continue; |
| if (VfsUtil.isAncestor(parent, file, false)) { |
| myInfoByParentDirectoryPath.get(file) |
| .clearFilter(); |
| } |
| } |
| fileStatusesChanged(); |
| } |
| |
| private static void fileStatusesChanged() { |
| Project[] openProjects = ProjectManager.getInstance().getOpenProjects(); |
| for (Project project : openProjects) { |
| VcsDirtyScopeManager.getInstance(project).markEverythingDirty(); |
| } |
| } |
| |
| private static boolean isCvsIgnoreFile(VirtualFile file) { |
| return CvsUtil.CVS_IGNORE_FILE.equals(file.getName()); |
| } |
| |
| public IgnoredFilesInfo getFilter(VirtualFile parent) { |
| return getInfoFor(parent).getIgnoreFilter(); |
| } |
| |
| public void beforeFileDeletion(@NotNull VirtualFileEvent event) { |
| processEvent(event); |
| } |
| |
| public void beforeFileMovement(@NotNull VirtualFileMoveEvent event) { |
| processEvent(event); |
| } |
| |
| public void fileCreated(@NotNull VirtualFileEvent event) { |
| processEvent(event); |
| } |
| |
| private void processEvent(VirtualFileEvent event) { |
| VirtualFile file = event.getFile(); |
| |
| if (isUserHomeCvsIgnoreFile(file)) { |
| myUserDirIgnores.clearInfo(); |
| fileStatusesChanged(); |
| return; |
| } |
| |
| if (isCvsIgnoreFile(file)) { |
| clearCachedFiltersFor(file.getParent()); |
| return; |
| } |
| |
| if (isCvsAdminDir(file.getParent())) { |
| VirtualFile parent = file.getParent().getParent(); |
| clearCachedEntriesFor(parent); |
| return; |
| } |
| |
| if (isCvsAdminDir(file)) { |
| clearCachedEntriesFor(file.getParent()); |
| return; |
| } |
| |
| if (file.isDirectory()) { |
| clearCachedEntriesRecursive(file); |
| } |
| } |
| |
| private static boolean isCvsAdminDir(VirtualFile file) { |
| if (file == null) return false; |
| return file.isDirectory() && CVS_ADMIN_DIRECTORY_NAME.equals(file.getName()); |
| } |
| |
| private synchronized void clearCachedEntriesRecursive(VirtualFile parent) { |
| if (!parent.isDirectory()) return; |
| |
| for (final VirtualFile file : myInfoByParentDirectoryPath.keySet()) { |
| if (file == null) continue; |
| if (!file.isValid()) continue; |
| if (VfsUtil.isAncestor(parent, file, false)) clearCachedEntriesFor(file); |
| } |
| } |
| |
| public Entry getEntryFor(VirtualFile parent, String name) { |
| return getCvsInfo(parent).getEntryNamed(name); |
| } |
| |
| public Entry getEntryFor(@NotNull VirtualFile file) { |
| final CvsInfo cvsInfo = getCvsInfo(file.getParent()); |
| assert(cvsInfo != null); |
| return cvsInfo.getEntryNamed(file.getName()); |
| } |
| |
| public void clearCachedEntriesFor(final VirtualFile parent) { |
| if (parent == null) return; |
| |
| CvsInfo cvsInfo = getInfoFor(parent); |
| cvsInfo.clearFilter(); |
| if (cvsInfo.isLoaded()) { |
| cvsInfo.clearAll(); |
| ApplicationManager.getApplication().invokeLater(new Runnable() { |
| public void run() { |
| if (parent.isValid()) { |
| onEntriesChanged(parent); |
| } |
| } |
| }); |
| } |
| } |
| |
| @Nullable |
| public Entry getCachedEntry(VirtualFile parent, String fileName){ |
| if (parent == null) return null; |
| |
| CvsInfo cvsInfo = getInfoFor(parent); |
| |
| if (!cvsInfo.isLoaded()) return null; |
| return cvsInfo.getEntryNamed(fileName); |
| } |
| |
| public void setEntryForFile(final VirtualFile parent, final Entry entry) { |
| if (parent == null) return; |
| |
| CvsInfo cvsInfo = getInfoFor(parent); |
| |
| if (!cvsInfo.isLoaded()) return; |
| |
| cvsInfo.setEntryAndReturnReplacedEntry(entry); |
| |
| ApplicationManager.getApplication().invokeLater(new Runnable() { |
| public void run() { |
| final VirtualFile file = CvsVfsUtil.findChild(parent, entry.getFileName()); |
| if (file != null) { |
| onEntryChanged(file); |
| } |
| } |
| }); |
| } |
| |
| public void removeEntryForFile(final File parent, final String fileName) { |
| CvsInfo cvsInfo = getInfoFor(CvsVfsUtil.findFileByIoFile(parent)); |
| if (!cvsInfo.isLoaded()) return; |
| |
| cvsInfo.removeEntryNamed(fileName); |
| |
| final VirtualFile[] file = new VirtualFile[1]; |
| |
| ApplicationManager.getApplication().invokeLater(new Runnable() { |
| public void run() { |
| ApplicationManager.getApplication().runReadAction(new Runnable() { |
| public void run() { |
| file[0] = LocalFileSystem.getInstance().findFileByIoFile(new File(parent, fileName)); |
| } |
| }); |
| if (file[0] != null) { |
| onEntryChanged(file[0]); |
| } |
| } |
| }); |
| } |
| |
| private void onEntriesChanged(final VirtualFile parent) { |
| final CvsEntriesListener[] listeners = myEntriesListeners.toArray(new CvsEntriesListener[myEntriesListeners.size()]); |
| for (CvsEntriesListener listener : listeners) { |
| listener.entriesChanged(parent); |
| } |
| } |
| |
| private void onEntryChanged(final VirtualFile file) { |
| final CvsEntriesListener[] listeners = myEntriesListeners.toArray(new CvsEntriesListener[myEntriesListeners.size()]); |
| for (CvsEntriesListener listener : listeners) { |
| listener.entryChanged(file); |
| } |
| } |
| |
| public void watchForCvsAdminFiles(final VirtualFile parent) { |
| if (parent == null) return; |
| synchronized (myFilesToRefresh) { |
| myFilesToRefresh.add(parent.getPath() + "/" + CVS_ADMIN_DIRECTORY_NAME); |
| } |
| } |
| |
| |
| public Collection<Entry> getEntriesIn(VirtualFile parent) { |
| return getCvsInfo(parent).getEntries(); |
| |
| } |
| |
| private CvsInfo getCvsInfo(VirtualFile parent) { |
| if (! isActive()) { |
| throw new ProcessCanceledException(); |
| } |
| if (parent == null) return CvsInfo.getDummyCvsInfo(); |
| return getInfoFor(parent); |
| } |
| |
| public void addCvsEntriesListener(CvsEntriesListener listener) { |
| myEntriesListeners.add(listener); |
| } |
| |
| public void removeCvsEntriesListener(CvsEntriesListener listener) { |
| myEntriesListeners.remove(listener); |
| } |
| |
| public synchronized void clearAll() { |
| myInfoByParentDirectoryPath.clear(); |
| } |
| |
| public boolean fileIsIgnored(VirtualFile file) { |
| final VirtualFile parent = file.getParent(); |
| if (parent == null) { |
| return false; |
| } |
| if (CvsUtil.fileIsUnderCvs(file)) return false; |
| return getFilter(parent).shouldBeIgnored(file.getName()); |
| } |
| |
| private void ensureFilesCached() { |
| final String[] paths; |
| synchronized (myFilesToRefresh) { |
| paths = ArrayUtil.toStringArray(myFilesToRefresh); |
| myFilesToRefresh.clear(); |
| } |
| for (String path : paths) { |
| final VirtualFile virtualFile = LocalFileSystem.getInstance().findFileByPath(path); |
| if (virtualFile != null) virtualFile.getChildren(); |
| } |
| } |
| |
| public CvsConnectionSettings getCvsConnectionSettingsFor(VirtualFile root) { |
| return getInfoFor(root).getConnectionSettings(); |
| } |
| |
| public CvsConnectionSettings getCvsConnectionSettingsFor(@NotNull File root) { |
| return getCvsConnectionSettingsFor(CvsVfsUtil.refreshAndFindFileByIoFile(root)); |
| } |
| |
| public CvsInfo getCvsInfoFor(VirtualFile directory) { |
| return getInfoFor(directory); |
| } |
| |
| public CvsConnectionSettings createConnectionSettingsOn(String cvsRoot) { |
| if (!myStringToSettingsMap.containsKey(cvsRoot)) { |
| final CvsRootConfiguration rootConfiguration = myApplicationLevelConfiguration.getConfigurationForCvsRoot(cvsRoot); |
| CvsConnectionSettings settings = new IDEARootFormatter(rootConfiguration).createConfiguration(); |
| myStringToSettingsMap.put(cvsRoot, settings); |
| } |
| return myStringToSettingsMap.get(cvsRoot); |
| } |
| |
| public UserDirIgnores getUserDirIgnores() { |
| return myUserDirIgnores; |
| } |
| |
| private static void fireStatusChanged(VirtualFile file) { |
| Project[] openProjects = ProjectManager.getInstance().getOpenProjects(); |
| for (Project project : openProjects) { |
| FileStatusManager.getInstance(project).fileStatusChanged(file); |
| VcsDirtyScopeManager.getInstance(project).fileDirty(file); |
| } |
| } |
| |
| private static boolean isUserHomeCvsIgnoreFile(VirtualFile file) { |
| return UserDirIgnores.userHomeCvsIgnoreFile().equals(CvsVfsUtil.getFileFor(file)); |
| } |
| |
| public boolean isActive() { |
| return myIsActive > 0; |
| } |
| |
| public String getRepositoryFor(VirtualFile root) { |
| return getInfoFor(root).getRepository(); |
| } |
| |
| public void cacheCvsAdminInfoIn(VirtualFile root) { |
| getInfoFor(root).cacheAll(); |
| } |
| |
| public String getStickyTagFor(VirtualFile fileByIoFile) { |
| return getCvsInfo(fileByIoFile).getStickyTag(); |
| } |
| |
| public void encodingChanged() { |
| if (!isActive()) return; |
| clearAll(); |
| fileStatusesChanged(); |
| } |
| } |
| |
| |