summaryrefslogtreecommitdiff
path: root/xml/impl/src/org/jetbrains/builtInWebServer/WebServerPathToFileManager.java
blob: 987fdcd3a139dc0ff8060197168dffd0b6862d02 (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
package org.jetbrains.builtInWebServer;

import com.google.common.cache.Cache;
import com.google.common.cache.CacheBuilder;
import com.intellij.ProjectTopics;
import com.intellij.openapi.application.Application;
import com.intellij.openapi.components.ServiceManager;
import com.intellij.openapi.project.Project;
import com.intellij.openapi.roots.ModuleRootAdapter;
import com.intellij.openapi.roots.ModuleRootEvent;
import com.intellij.openapi.vfs.VirtualFile;
import com.intellij.openapi.vfs.VirtualFileManager;
import com.intellij.openapi.vfs.newvfs.BulkFileListener;
import com.intellij.openapi.vfs.newvfs.events.VFileContentChangeEvent;
import com.intellij.openapi.vfs.newvfs.events.VFileEvent;
import com.intellij.util.PairFunction;
import org.jetbrains.annotations.NotNull;
import org.jetbrains.annotations.Nullable;

import java.util.List;
import java.util.concurrent.TimeUnit;

/**
 * Implement {@link WebServerRootsProvider} to add your provider
 */
public class WebServerPathToFileManager {
  private static final PairFunction<String, VirtualFile, VirtualFile> RELATIVE_PATH_RESOLVER = new PairFunction<String, VirtualFile, VirtualFile>() {
    @Nullable
    @Override
    public VirtualFile fun(String path, VirtualFile parent) {
      return parent.findFileByRelativePath(path);
    }
  };

  private static final PairFunction<String, VirtualFile, VirtualFile> EMPTY_PATH_RESOLVER = new PairFunction<String, VirtualFile, VirtualFile>() {
    @Nullable
    @Override
    public VirtualFile fun(String path, VirtualFile parent) {
      return BuiltInWebServer.findIndexFile(parent);
    }
  };

  private final Project project;

  final Cache<String, VirtualFile> pathToFileCache = CacheBuilder.newBuilder().maximumSize(512).expireAfterAccess(10, TimeUnit.MINUTES).build();
  // time to expire should be greater than pathToFileCache
  private final Cache<VirtualFile, PathInfo> fileToRoot = CacheBuilder.newBuilder().maximumSize(512).expireAfterAccess(11, TimeUnit.MINUTES).build();

  public static WebServerPathToFileManager getInstance(@NotNull Project project) {
    return ServiceManager.getService(project, WebServerPathToFileManager.class);
  }

  public WebServerPathToFileManager(@NotNull Application application, @NotNull Project project) {
    this.project = project;
    application.getMessageBus().connect(project).subscribe(VirtualFileManager.VFS_CHANGES, new BulkFileListener.Adapter() {
      @Override
      public void after(@NotNull List<? extends VFileEvent> events) {
        for (VFileEvent event : events) {
          if (event instanceof VFileContentChangeEvent) {
            VirtualFile file = ((VFileContentChangeEvent)event).getFile();
            for (WebServerRootsProvider rootsProvider : WebServerRootsProvider.EP_NAME.getExtensions()) {
              if (rootsProvider.isClearCacheOnFileContentChanged(file)) {
                clearCache();
                break;
              }
            }
          }
          else {
            clearCache();
            break;
          }
        }
      }
    });
    project.getMessageBus().connect().subscribe(ProjectTopics.PROJECT_ROOTS, new ModuleRootAdapter() {
      @Override
      public void rootsChanged(ModuleRootEvent event) {
        clearCache();
      }
    });
  }

  private void clearCache() {
    pathToFileCache.invalidateAll();
    fileToRoot.invalidateAll();
  }

  @Nullable
  public VirtualFile get(@NotNull String path) {
    return get(path, true);
  }

  @Nullable
  public VirtualFile get(@NotNull String path, boolean cacheResult) {
    VirtualFile result = pathToFileCache.getIfPresent(path);
    if (result == null || !result.isValid()) {
      result = findByRelativePath(project, path);
      if (cacheResult && result != null && result.isValid()) {
        pathToFileCache.put(path, result);
      }
    }
    return result;
  }

  @Nullable
  public String getPath(@NotNull VirtualFile file) {
    PathInfo pathInfo = getRoot(file);
    return pathInfo == null ? null : pathInfo.getPath();
  }

  @Nullable
  public PathInfo getRoot(@NotNull VirtualFile child) {
    PathInfo result = fileToRoot.getIfPresent(child);
    if (result == null) {
      for (WebServerRootsProvider rootsProvider : WebServerRootsProvider.EP_NAME.getExtensions()) {
        result = rootsProvider.getRoot(child, project);
        if (result != null) {
          fileToRoot.put(child, result);
          break;
        }
      }
    }
    return result;
  }

  @Nullable
  VirtualFile findByRelativePath(@NotNull Project project, @NotNull String path) {
    for (WebServerRootsProvider rootsProvider : WebServerRootsProvider.EP_NAME.getExtensions()) {
      PathInfo result = rootsProvider.resolve(path, project);
      if (result != null) {
        fileToRoot.put(result.getChild(), result);
        return result.getChild();
      }
    }
    return null;
  }

  @NotNull
  public PairFunction<String, VirtualFile, VirtualFile> getResolver(@NotNull String path) {
    return path.isEmpty() ? EMPTY_PATH_RESOLVER : RELATIVE_PATH_RESOLVER;
  }
}