2 * Copyright 2000-2014 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.openapi.vfs.ex.dummy;
18 import com.intellij.openapi.Disposable;
19 import com.intellij.openapi.application.Application;
20 import com.intellij.openapi.application.ApplicationManager;
21 import com.intellij.openapi.diagnostic.Logger;
22 import com.intellij.openapi.project.Project;
23 import com.intellij.openapi.project.ProjectManager;
24 import com.intellij.openapi.project.ProjectManagerAdapter;
25 import com.intellij.openapi.util.Disposer;
26 import com.intellij.openapi.util.text.StringUtil;
27 import com.intellij.openapi.vfs.VirtualFile;
28 import com.intellij.util.containers.ConcurrentFactoryMap;
29 import com.intellij.util.containers.ContainerUtil;
30 import com.intellij.util.containers.FactoryMap;
31 import org.jetbrains.annotations.NotNull;
32 import org.jetbrains.annotations.Nullable;
33 import org.jetbrains.annotations.TestOnly;
35 import java.io.IOException;
36 import java.util.Collection;
37 import java.util.concurrent.ConcurrentMap;
42 public abstract class DummyCachingFileSystem<T extends VirtualFile> extends DummyFileSystem {
43 private static final Logger LOG = Logger.getInstance(DummyCachingFileSystem.class);
45 private final String myProtocol;
47 private final ConcurrentMap<String, Project> myProjectMap = ContainerUtil.newConcurrentMap();
49 private final FactoryMap<String, T> myCachedFiles = new ConcurrentFactoryMap<String, T>() {
51 protected T create(String key) {
52 if (ApplicationManager.getApplication().isUnitTestMode()) {
53 //noinspection TestOnlyProblems
57 return findFileByPathInner(key);
61 public T get(Object key) {
62 T file = super.get(key);
63 if (file != null && !file.isValid()) {
65 return super.get(key);
71 public DummyCachingFileSystem(String protocol) {
72 myProtocol = protocol;
74 final Application application = ApplicationManager.getApplication();
75 application.getMessageBus().connect(application).subscribe(ProjectManager.TOPIC, new ProjectManagerAdapter() {
77 public void projectOpened(final Project project) {
78 onProjectOpened(project);
86 public final String getProtocol() {
92 public final VirtualFile createRoot(String name) {
97 public final T findFileByPath(@NotNull String path) {
98 return myCachedFiles.get(path);
103 public String extractPresentableUrl(@NotNull String path) {
104 VirtualFile file = findFileByPath(path);
105 return file != null ? getPresentableUrl(file) : super.extractPresentableUrl(path);
108 protected String getPresentableUrl(@NotNull VirtualFile file) {
109 return file.getPresentableName();
112 protected abstract T findFileByPathInner(@NotNull String path);
114 protected void doRenameFile(VirtualFile vFile, String newName) {
115 throw new UnsupportedOperationException("not implemented");
119 public Project getProject(@Nullable String projectId) {
120 return myProjectMap.get(projectId);
124 public Project getProjectOrFail(String projectId) {
125 Project project = myProjectMap.get(projectId);
126 if (project == null) {
127 throw new AssertionError(String.format("'%s' project not found among %s", projectId,
128 ContainerUtil.newArrayList(myProjectMap.keySet())));
134 public Collection<T> getCachedFiles() {
135 return myCachedFiles.notNullValues();
139 public void onProjectClosed(@NotNull Project project) {
140 onProjectClosedInner(project);
143 protected void onProjectClosedInner(@NotNull Project project) {
144 String projectId = project.getLocationHash();
145 Project mapped = myProjectMap.remove(projectId);
146 if (mapped == null) {
147 LOG.warn(String.format("'%s' project not mapped", projectId));
152 public void onProjectOpened(final Project project) {
153 // use Disposer instead of ProjectManagerListener#projectClosed() because Disposer.dispose(project)
154 // is called later and some cached files should stay valid till the last moment
155 Disposer.register(project, new Disposable() {
157 public void dispose() {
158 onProjectClosedInner(project);
163 String projectId = project.getLocationHash();
164 Project mapped = myProjectMap.put(projectId, project);
166 if (mapped != null) {
167 LOG.error(String.format("'%s' project rebound, was %s", projectId, mapped));
171 private void initProjectMap() {
172 for (Project project : ProjectManager.getInstance().getOpenProjects()) {
173 if (project.isOpen()) onProjectOpened(project);
177 protected void clearCache() {
181 protected void clearInvalidFiles() {
182 for (T t : myCachedFiles.notNullValues()) {
183 if (!t.isValid()) myCachedFiles.removeValue(t);
185 //noinspection StatementWithEmptyBody
186 while (myCachedFiles.removeValue(null)) ;
190 public void cleanup() {
191 myCachedFiles.clear();
192 myProjectMap.clear();
196 public void renameFile(Object requestor, @NotNull VirtualFile vFile, @NotNull String newName) throws IOException {
197 String oldName = vFile.getName();
198 beforeFileRename(vFile, requestor, oldName, newName);
200 doRenameFile(vFile, newName);
203 fileRenamed(vFile, requestor, oldName, newName);
207 protected void beforeFileRename(@NotNull VirtualFile file, Object requestor, @NotNull String oldName, @NotNull String newName) {
208 fireBeforePropertyChange(requestor, file, VirtualFile.PROP_NAME, oldName, newName);
209 myCachedFiles.remove(file.getPath());
212 protected void fileRenamed(@NotNull VirtualFile file, Object requestor, String oldName, String newName) {
213 //noinspection unchecked
214 myCachedFiles.put(file.getPath(), (T)file);
215 firePropertyChanged(requestor, file, VirtualFile.PROP_NAME, oldName, newName);
218 protected static String escapeSlash(String name) {
219 return name == null ? "" : StringUtil.replace(name, "/", "&slash;");
222 protected static String unescapeSlash(String name) {
223 return name == null ? "" : StringUtil.replace(name, "&slash;", "/");