2 * Copyright 2000-2009 JetBrains s.r.o.
4 * Licensed under the Apache License, Version 2.0 (the "License");
5 * you may not use this file except in compliance with the License.
6 * You may obtain a copy of the License at
8 * http://www.apache.org/licenses/LICENSE-2.0
10 * Unless required by applicable law or agreed to in writing, software
11 * distributed under the License is distributed on an "AS IS" BASIS,
12 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 * See the License for the specific language governing permissions and
14 * limitations under the License.
16 package com
.intellij
.cvsSupport2
.application
;
18 import com
.intellij
.cvsSupport2
.CvsUtil
;
19 import com
.intellij
.cvsSupport2
.config
.CvsApplicationLevelConfiguration
;
20 import com
.intellij
.cvsSupport2
.config
.CvsRootConfiguration
;
21 import com
.intellij
.cvsSupport2
.connections
.CvsConnectionSettings
;
22 import com
.intellij
.cvsSupport2
.connections
.IDEARootFormatter
;
23 import com
.intellij
.cvsSupport2
.cvsIgnore
.IgnoredFilesInfo
;
24 import com
.intellij
.cvsSupport2
.cvsIgnore
.UserDirIgnores
;
25 import com
.intellij
.cvsSupport2
.cvsstatuses
.CvsEntriesListener
;
26 import com
.intellij
.cvsSupport2
.util
.CvsVfsUtil
;
27 import com
.intellij
.openapi
.application
.ApplicationManager
;
28 import com
.intellij
.openapi
.components
.ServiceManager
;
29 import com
.intellij
.openapi
.diagnostic
.Logger
;
30 import com
.intellij
.openapi
.progress
.ProcessCanceledException
;
31 import com
.intellij
.openapi
.project
.Project
;
32 import com
.intellij
.openapi
.project
.ProjectManager
;
33 import com
.intellij
.openapi
.vcs
.FileStatusManager
;
34 import com
.intellij
.openapi
.vcs
.changes
.VcsDirtyScopeManager
;
35 import com
.intellij
.openapi
.vfs
.*;
36 import com
.intellij
.util
.ArrayUtil
;
37 import com
.intellij
.util
.containers
.HashMap
;
38 import com
.intellij
.util
.containers
.HashSet
;
39 import org
.jetbrains
.annotations
.NotNull
;
40 import org
.jetbrains
.annotations
.Nullable
;
41 import org
.netbeans
.lib
.cvsclient
.admin
.Entry
;
44 import java
.util
.ArrayList
;
45 import java
.util
.Collection
;
52 public class CvsEntriesManager
extends VirtualFileAdapter
{
54 private static final Logger LOG
= Logger
.getInstance("#com.intellij.cvsSupport2.application.CvsEntriesManager");
56 private final Map
<VirtualFile
, CvsInfo
> myInfoByParentDirectoryPath
= new HashMap
<VirtualFile
, CvsInfo
>();
58 private static final String CVS_ADMIN_DIRECTORY_NAME
= CvsUtil
.CVS
;
60 private final Collection
<CvsEntriesListener
> myEntriesListeners
= new ArrayList
<CvsEntriesListener
>();
61 private int myIsActive
= 0;
62 private final Collection
<String
> myFilesToRefresh
= new HashSet
<String
>();
63 private int mySynchronizationActionLocks
= 0;
65 private final Map
<String
, CvsConnectionSettings
> myStringToSettingsMap
= new HashMap
<String
, CvsConnectionSettings
>();
66 private final UserDirIgnores myUserDirIgnores
= new UserDirIgnores();
67 private final MyVirtualFileManagerListener myVirtualFileManagerListener
= new MyVirtualFileManagerListener();
68 private final CvsApplicationLevelConfiguration myApplicationLevelConfiguration
;
70 public static CvsEntriesManager
getInstance() {
71 return ServiceManager
.getService(CvsEntriesManager
.class);
74 public CvsEntriesManager(final CvsApplicationLevelConfiguration applicationLevelConfiguration
) {
75 myApplicationLevelConfiguration
= applicationLevelConfiguration
;
78 private class MyVirtualFileManagerListener
implements VirtualFileManagerListener
{
79 public void afterRefreshFinish(boolean asynchonous
) {
80 ensureFilesCached(); //to cache for next refreshes
83 public void beforeRefreshStart(boolean asynchonous
) {
87 public void registerAsVirtualFileListener() {
88 if (myIsActive
== 0) {
89 VirtualFileManager
.getInstance().addVirtualFileListener(this);
90 VirtualFileManager
.getInstance().addVirtualFileManagerListener(myVirtualFileManagerListener
);
95 public synchronized void unregisterAsVirtualFileListener() {
96 LOG
.assertTrue(isActive());
98 if (myIsActive
== 0) {
99 VirtualFileManager
.getInstance().removeVirtualFileListener(this);
100 VirtualFileManager
.getInstance().removeVirtualFileManagerListener(myVirtualFileManagerListener
);
101 myInfoByParentDirectoryPath
.clear();
105 public void beforePropertyChange(VirtualFilePropertyEvent event
) {
109 public void beforeContentsChange(VirtualFileEvent event
) {
113 public void contentsChanged(VirtualFileEvent event
) {
114 fireStatusChanged(event
.getFile());
119 private synchronized CvsInfo
getInfoFor(VirtualFile parent
) {
120 if (!myInfoByParentDirectoryPath
.containsKey(parent
)) {
121 CvsInfo cvsInfo
= new CvsInfo(parent
, this);
122 myInfoByParentDirectoryPath
.put(cvsInfo
.getKey(), cvsInfo
);
124 return myInfoByParentDirectoryPath
.get(parent
);
127 public synchronized void clearCachedFiltersFor(final VirtualFile parent
) {
128 for (final VirtualFile file
: myInfoByParentDirectoryPath
.keySet()) {
129 if (file
== null) continue;
130 if (!file
.isValid()) continue;
131 if (VfsUtil
.isAncestor(parent
, file
, false)) {
132 myInfoByParentDirectoryPath
.get(file
)
136 fileStatusesChanged();
139 private static void fileStatusesChanged() {
140 Project
[] openProjects
= ProjectManager
.getInstance().getOpenProjects();
141 for (Project project
: openProjects
) {
142 VcsDirtyScopeManager
.getInstance(project
).markEverythingDirty();
146 private static boolean isCvsIgnoreFile(VirtualFile file
) {
147 return CvsUtil
.CVS_IGNORE_FILE
.equals(file
.getName());
150 public IgnoredFilesInfo
getFilter(VirtualFile parent
) {
151 return getInfoFor(parent
).getIgnoreFilter();
154 public void beforeFileDeletion(VirtualFileEvent event
) {
158 public void beforeFileMovement(VirtualFileMoveEvent event
) {
162 public void fileCreated(VirtualFileEvent event
) {
166 private void processEvent(VirtualFileEvent event
) {
167 VirtualFile file
= event
.getFile();
169 if (isUserHomeCvsIgnoreFile(file
)) {
170 myUserDirIgnores
.clearInfo();
171 fileStatusesChanged();
175 if (isCvsIgnoreFile(file
)) {
176 clearCachedFiltersFor(file
.getParent());
180 if (isCvsAdminDir(file
.getParent())) {
181 VirtualFile parent
= file
.getParent().getParent();
182 clearCachedEntriesFor(parent
);
186 if (isCvsAdminDir(file
)) {
187 clearCachedEntriesFor(file
.getParent());
191 if (file
.isDirectory()) {
192 clearCachedEntriesRecursive(file
);
196 private static boolean isCvsAdminDir(VirtualFile file
) {
197 if (file
== null) return false;
198 return file
.isDirectory() && CVS_ADMIN_DIRECTORY_NAME
.equals(file
.getName());
201 private synchronized void clearCachedEntriesRecursive(VirtualFile parent
) {
202 if (!parent
.isDirectory()) return;
204 for (final VirtualFile file
: myInfoByParentDirectoryPath
.keySet()) {
205 if (file
== null) continue;
206 if (!file
.isValid()) continue;
207 if (VfsUtil
.isAncestor(parent
, file
, false)) clearCachedEntriesFor(file
);
211 public Entry
getEntryFor(VirtualFile parent
, String name
) {
212 return getCvsInfo(parent
).getEntryNamed(name
);
215 public Entry
getEntryFor(@NotNull VirtualFile file
) {
216 final CvsInfo cvsInfo
= getCvsInfo(file
.getParent());
217 assert(cvsInfo
!= null);
218 return cvsInfo
.getEntryNamed(file
.getName());
221 public void clearCachedEntriesFor(final VirtualFile parent
) {
222 if (parent
== null) return;
224 CvsInfo cvsInfo
= getInfoFor(parent
);
225 cvsInfo
.clearFilter();
226 if (cvsInfo
.isLoaded()) {
228 ApplicationManager
.getApplication().invokeLater(new Runnable() {
230 if (parent
.isValid()) {
231 onEntriesChanged(parent
);
239 public Entry
getCachedEntry(VirtualFile parent
, String fileName
){
240 if (parent
== null) return null;
242 CvsInfo cvsInfo
= getInfoFor(parent
);
244 if (!cvsInfo
.isLoaded()) return null;
245 return cvsInfo
.getEntryNamed(fileName
);
248 public void setEntryForFile(final VirtualFile parent
, final Entry entry
) {
249 if (parent
== null) return;
251 CvsInfo cvsInfo
= getInfoFor(parent
);
253 if (!cvsInfo
.isLoaded()) return;
255 cvsInfo
.setEntryAndReturnReplacedEntry(entry
);
257 ApplicationManager
.getApplication().invokeLater(new Runnable() {
259 final VirtualFile file
= CvsVfsUtil
.findChild(parent
, entry
.getFileName());
261 onEntryChanged(file
);
267 public void removeEntryForFile(final File parent
, final String fileName
) {
268 CvsInfo cvsInfo
= getInfoFor(CvsVfsUtil
.findFileByIoFile(parent
));
269 if (!cvsInfo
.isLoaded()) return;
271 cvsInfo
.removeEntryNamed(fileName
);
273 final VirtualFile
[] file
= new VirtualFile
[1];
275 ApplicationManager
.getApplication().invokeLater(new Runnable() {
277 ApplicationManager
.getApplication().runReadAction(new Runnable() {
279 file
[0] = LocalFileSystem
.getInstance().findFileByIoFile(new File(parent
, fileName
));
282 if (file
[0] != null) {
283 onEntryChanged(file
[0]);
289 private void onEntriesChanged(final VirtualFile parent
) {
290 final CvsEntriesListener
[] listeners
= myEntriesListeners
.toArray(new CvsEntriesListener
[myEntriesListeners
.size()]);
291 for (CvsEntriesListener listener
: listeners
) {
292 listener
.entriesChanged(parent
);
296 private void onEntryChanged(final VirtualFile file
) {
297 final CvsEntriesListener
[] listeners
= myEntriesListeners
.toArray(new CvsEntriesListener
[myEntriesListeners
.size()]);
298 for (CvsEntriesListener listener
: listeners
) {
299 listener
.entryChanged(file
);
303 public void watchForCvsAdminFiles(final VirtualFile parent
) {
304 if (parent
== null) return;
305 synchronized (myFilesToRefresh
) {
306 myFilesToRefresh
.add(parent
.getPath() + "/" + CVS_ADMIN_DIRECTORY_NAME
);
311 public Collection
<Entry
> getEntriesIn(VirtualFile parent
) {
312 return getCvsInfo(parent
).getEntries();
316 private CvsInfo
getCvsInfo(VirtualFile parent
) {
318 throw new ProcessCanceledException();
320 if (parent
== null) return CvsInfo
.getDummyCvsInfo();
321 return getInfoFor(parent
);
324 public void addCvsEntriesListener(CvsEntriesListener listener
) {
325 myEntriesListeners
.add(listener
);
328 public void removeCvsEntriesListener(CvsEntriesListener listener
) {
329 myEntriesListeners
.remove(listener
);
332 public synchronized void clearAll() {
333 myInfoByParentDirectoryPath
.clear();
336 public boolean fileIsIgnored(VirtualFile file
) {
337 VirtualFile parent
= file
.getParent();
338 if (parent
== null) {
341 if (CvsUtil
.fileIsUnderCvs(file
)) return false;
342 return getFilter(parent
).shouldBeIgnored(file
.getName());
345 public void lockSynchronizationActions() {
346 mySynchronizationActionLocks
++;
349 public void unlockSynchronizationActions() {
350 LOG
.assertTrue(mySynchronizationActionLocks
> 0);
351 mySynchronizationActionLocks
--;
354 private void ensureFilesCached() {
356 synchronized (myFilesToRefresh
) {
357 paths
= ArrayUtil
.toStringArray(myFilesToRefresh
);
358 myFilesToRefresh
.clear();
360 for (String path
: paths
) {
361 VirtualFile virtualFile
= LocalFileSystem
.getInstance().findFileByPath(path
);
362 if (virtualFile
!= null) virtualFile
.getChildren();
366 public CvsConnectionSettings
getCvsConnectionSettingsFor(VirtualFile root
) {
367 return getInfoFor(root
).getConnectionSettings();
370 public CvsConnectionSettings
getCvsConnectionSettingsFor(@NotNull File root
) {
371 return getCvsConnectionSettingsFor(CvsVfsUtil
.refreshAndFindFileByIoFile(root
));
374 public CvsInfo
getCvsInfoFor(VirtualFile directory
) {
375 return getInfoFor(directory
);
378 public CvsConnectionSettings
createConnectionSettingsOn(String cvsRoot
) {
379 if (!myStringToSettingsMap
.containsKey(cvsRoot
)) {
380 final CvsRootConfiguration rootConfiguration
= myApplicationLevelConfiguration
.getConfigurationForCvsRoot(cvsRoot
);
381 CvsConnectionSettings settings
= new IDEARootFormatter(rootConfiguration
).createConfiguration();
382 myStringToSettingsMap
.put(cvsRoot
, settings
);
384 return myStringToSettingsMap
.get(cvsRoot
);
387 public UserDirIgnores
getUserdIgnores() {
388 return myUserDirIgnores
;
391 private static void fireStatusChanged(VirtualFile file
) {
392 Project
[] openProjects
= ProjectManager
.getInstance().getOpenProjects();
393 for (Project project
: openProjects
) {
394 FileStatusManager
.getInstance(project
).fileStatusChanged(file
);
395 VcsDirtyScopeManager
.getInstance(project
).fileDirty(file
);
399 private boolean isUserHomeCvsIgnoreFile(VirtualFile file
) {
400 return myUserDirIgnores
.userHomeCvsIgnoreFile().equals(CvsVfsUtil
.getFileFor(file
));
403 public boolean isActive() {
404 return myIsActive
> 0;
407 public String
getRepositoryFor(VirtualFile root
) {
408 return getInfoFor(root
).getRepository();
411 public void cacheCvsAdminInfoIn(VirtualFile root
) {
412 getInfoFor(root
).cacheAll();
415 public String
getStickyTagFor(VirtualFile fileByIoFile
) {
416 return getCvsInfo(fileByIoFile
).getStickyTag();
419 public void encodingChanged() {
420 if (!isActive()) return;
422 fileStatusesChanged();