/* * Copyright 2000-2009 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 org.community.intellij.plugins.communitycase.vfs; import com.intellij.openapi.project.Project; import com.intellij.openapi.vcs.ProjectLevelVcsManager; import com.intellij.openapi.vfs.*; import com.intellij.util.containers.HashSet; import org.community.intellij.plugins.communitycase.Util; import org.community.intellij.plugins.communitycase.Vcs; import org.jetbrains.annotations.Nullable; import java.util.Arrays; import java.util.Iterator; import java.util.Set; /** * The tracker for configuration files, it tracks the following events: * <ul> * <li>Changes in configuration files: ./config and ~/.config</li> * </ul> * The tracker assumes that roots are configured correctly. */ public class ConfigTracker implements RootsListener { /** * The context project */ private final Project myProject; /** * The vcs object */ private final Vcs myVcs; /** * The vcs manager that tracks content roots */ private final ProjectLevelVcsManager myVcsManager; /** * The listener collection (managed by Vcs object since lifetime of this object is less than lifetime of Vcs) */ private final ConfigListener myMulticaster; /** * The appeared roots that has been already reported as changed */ private final HashSet<VirtualFile> myReportedRoots = new HashSet<VirtualFile>(); /** * Local file system service */ private final LocalFileSystem myLocalFileSystem; /** * The file listener */ private final MyFileListener myFileListener; /** * The constructor * * @param project the context project * @param vcs the vcs object * @param multicaster the listener collection to use */ public ConfigTracker(Project project, Vcs vcs, ConfigListener multicaster) { myProject = project; myVcs = vcs; myMulticaster = multicaster; myLocalFileSystem = LocalFileSystem.getInstance(); myVcsManager = ProjectLevelVcsManager.getInstance(project); myVcs.addRootsListener(this); myFileListener = new MyFileListener(); VirtualFileManager.getInstance().addVirtualFileListener(myFileListener); RootsChanged(); } /** * This method is invoked when set of configured roots changed. */ public void RootsChanged() { VirtualFile[] contentRoots = myVcsManager.getRootsUnderVcs(myVcs); if (contentRoots == null || contentRoots.length == 0) { return; } Set<VirtualFile> currentRootSet = Util.rootsForPaths(Arrays.asList(contentRoots)); HashSet<VirtualFile> newRoots = new HashSet<VirtualFile>(currentRootSet); synchronized (myReportedRoots) { for (Iterator<VirtualFile> i = myReportedRoots.iterator(); i.hasNext();) { VirtualFile root = i.next(); if (!root.isValid()) { i.remove(); } } newRoots.removeAll(myReportedRoots); myReportedRoots.clear(); myReportedRoots.addAll(currentRootSet); } for (VirtualFile root : newRoots) { VirtualFile config = root.findFileByRelativePath("./config"); myMulticaster.configChanged(root, config); } // visit user home directory in order to notice .config changes later VirtualFile userHome = getUserHome(); if (userHome != null) { userHome.getChildren(); } } /** * @return user home directory */ @Nullable private VirtualFile getUserHome() { return myLocalFileSystem.findFileByPath(System.getProperty("user.home")); } /** * Dispose the tracker removing all registered listeners */ public void dispose() { myVcs.removeRootsListener(this); VirtualFileManager.getInstance().removeVirtualFileListener(myFileListener); } /** * The listener for the file system that checks if the configuration files are changed. * Note that events are checked in quite a shallow form. More radical events will cause * remapping of roots and RootsChanged() event will be delivered. */ private class MyFileListener extends VirtualFileAdapter { /** * Check if the event affects configuration files in registered roots * * @param file the file to check */ private void checkConfigAffected(VirtualFile file) { if (file.getName().equals(".config")) { VirtualFile userHome = getUserHome(); VirtualFile parent = file.getParent(); if (userHome != null && parent != null && parent.equals(userHome)) { HashSet<VirtualFile> allRoots; synchronized (myReportedRoots) { allRoots = new HashSet<VirtualFile>(myReportedRoots); } for (VirtualFile root : allRoots) { myMulticaster.configChanged(root, file); } } return; } VirtualFile base = Util.getPossibleBase(file, ".", "config"); if (base != null) { boolean reported; synchronized (myReportedRoots) { reported = myReportedRoots.contains(base); } if (reported) { myMulticaster.configChanged(base, file); } } } /** * {@inheritDoc} */ @Override public void fileCreated(VirtualFileEvent event) { checkConfigAffected(event.getFile()); } /** * {@inheritDoc} */ @Override public void beforeFileDeletion(VirtualFileEvent event) { checkConfigAffected(event.getFile()); } /** * {@inheritDoc} */ @Override public void contentsChanged(VirtualFileEvent event) { checkConfigAffected(event.getFile()); } /** * {@inheritDoc} */ @Override public void fileCopied(VirtualFileCopyEvent event) { super.fileCopied(event); } /** * {@inheritDoc} */ @Override public void fileMoved(VirtualFileMoveEvent event) { String fileName = event.getFileName(); VirtualFile newParent = event.getNewParent(); VirtualFile oldParent = event.getOldParent(); if (fileName.equals("config")) { checkParent(newParent); checkParent(oldParent); } if (fileName.equals(".config")) { VirtualFile userHome = getUserHome(); if (userHome != null && (newParent.equals(userHome) || oldParent.equals(userHome))) { HashSet<VirtualFile> allRoots; synchronized (myReportedRoots) { allRoots = new HashSet<VirtualFile>(myReportedRoots); } VirtualFile config = userHome.findChild(".config"); for (VirtualFile root : allRoots) { myMulticaster.configChanged(root, config); } } } } /** * Check parent and report event if it is one of reported roots * * @param parent the parent to check */ private void checkParent(VirtualFile parent) { if (parent.getName().equals(".")) { VirtualFile base = parent.getParent(); if (base != null) { boolean reported; synchronized (myReportedRoots) { reported = myReportedRoots.contains(base); } if (reported) { myMulticaster.configChanged(base, parent.findChild("config")); } } } } } }