cleanup
[idea/community.git] / python / python-psi-impl / src / com / jetbrains / python / psi / resolve / PythonSdkPathCache.java
1 /*
2  * Copyright 2000-2017 JetBrains s.r.o.
3  *
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
7  *
8  * http://www.apache.org/licenses/LICENSE-2.0
9  *
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.
15  */
16 package com.jetbrains.python.psi.resolve;
17
18 import com.intellij.openapi.Disposable;
19 import com.intellij.openapi.module.Module;
20 import com.intellij.openapi.module.ModuleManager;
21 import com.intellij.openapi.project.Project;
22 import com.intellij.openapi.projectRoots.ProjectJdkTable;
23 import com.intellij.openapi.projectRoots.Sdk;
24 import com.intellij.openapi.util.Disposer;
25 import com.intellij.openapi.util.Key;
26 import com.intellij.openapi.vfs.VirtualFileManager;
27 import com.intellij.util.containers.ContainerUtil;
28 import com.intellij.util.messages.MessageBusConnection;
29 import com.jetbrains.python.packaging.PyPackageManager;
30 import com.jetbrains.python.psi.impl.PyBuiltinCache;
31 import org.jetbrains.annotations.NotNull;
32
33 import java.util.Map;
34 import java.util.concurrent.atomic.AtomicReference;
35
36 /**
37  * @author yole
38  */
39 public class PythonSdkPathCache extends PythonPathCache implements Disposable {
40   private static final Key<Map<Project, PythonSdkPathCache>> KEY = Key.create("PythonPathCache");
41
42   public static PythonSdkPathCache getInstance(@NotNull Project project, @NotNull Sdk sdk) {
43     synchronized (KEY) {
44       Map<Project, PythonSdkPathCache> cacheMap = sdk.getUserData(KEY);
45       if (cacheMap == null) {
46         cacheMap = ContainerUtil.createWeakMap();
47         sdk.putUserData(KEY, cacheMap);
48       }
49       PythonSdkPathCache cache = cacheMap.get(project);
50       if (cache == null) {
51         cache = new PythonSdkPathCache(project, sdk);
52         cacheMap.put(project, cache);
53       }
54       return cache;
55     }
56   }
57
58   private final Project myProject;
59   private final Sdk mySdk;
60   private final AtomicReference<PyBuiltinCache> myBuiltins = new AtomicReference<>();
61
62   public PythonSdkPathCache(@NotNull final Project project, @NotNull final Sdk sdk) {
63     myProject = project;
64     mySdk = sdk;
65     if (project.isDisposed()) {
66       return;
67     }
68     Disposer.register(project, this);
69     final MessageBusConnection connection = project.getMessageBus().connect(this);
70     connection.subscribe(ProjectJdkTable.JDK_TABLE_TOPIC, new ProjectJdkTable.Listener() {
71       @Override
72       public void jdkRemoved(@NotNull Sdk jdk) {
73         if (jdk == sdk) {
74           Disposer.dispose(PythonSdkPathCache.this);
75         }
76       }
77     });
78     connection.subscribe(PyPackageManager.PACKAGE_MANAGER_TOPIC, eventSdk -> {
79       if (eventSdk == sdk) {
80         clearCache();
81       }
82     });
83     sdk.getRootProvider().addRootSetChangedListener(wrapper -> {
84       clearCache();
85       if (!project.isDisposed()) {
86         final Module[] modules = ModuleManager.getInstance(project).getModules();
87         for (Module module : modules) {
88           PythonModulePathCache.getInstance(module).clearCache();
89         }
90       }
91       myBuiltins.set(null);
92     }, this);
93     VirtualFileManager.getInstance().addVirtualFileListener(new MyVirtualFileListener(), this);
94   }
95
96   @Override
97   public void dispose() {
98     if (mySdk != null) {
99       synchronized (KEY) {
100         final Map<Project, PythonSdkPathCache> cacheMap = mySdk.getUserData(KEY);
101         if (cacheMap != null) {
102           cacheMap.remove(myProject);
103         }
104       }
105     }
106   }
107
108   @NotNull
109   public PyBuiltinCache getBuiltins() {
110     while (true) {
111       PyBuiltinCache pyBuiltinCache = myBuiltins.get();
112       if (pyBuiltinCache == null || !pyBuiltinCache.isValid()) {
113         PyBuiltinCache newCache = new PyBuiltinCache(PyBuiltinCache.getBuiltinsForSdk(myProject, mySdk),
114                                                      PyBuiltinCache.getExceptionsForSdk(myProject, mySdk));
115         if (myBuiltins.compareAndSet(pyBuiltinCache, newCache)) {
116           return newCache;
117         }
118       }
119       else {
120         return pyBuiltinCache;
121       }
122     }
123   }
124
125   public void clearBuiltins() {
126     myBuiltins.set(null);
127   }
128 }