Move ModulePointerManagerImpl to projectModel-impl
[idea/community.git] / plugins / groovy / src / org / jetbrains / plugins / groovy / lang / psi / impl / GroovyPsiManager.java
1 /*
2  * Copyright 2000-2009 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
17 package org.jetbrains.plugins.groovy.lang.psi.impl;
18
19 import com.google.common.collect.Sets;
20 import com.intellij.ProjectTopics;
21 import com.intellij.openapi.components.ServiceManager;
22 import com.intellij.openapi.diagnostic.Logger;
23 import com.intellij.openapi.project.Project;
24 import com.intellij.openapi.roots.ModuleRootAdapter;
25 import com.intellij.openapi.roots.ModuleRootEvent;
26 import com.intellij.openapi.util.Computable;
27 import com.intellij.openapi.util.RecursionGuard;
28 import com.intellij.openapi.util.RecursionManager;
29 import com.intellij.psi.*;
30 import com.intellij.psi.impl.PsiManagerEx;
31 import com.intellij.psi.search.GlobalSearchScope;
32 import com.intellij.psi.util.InheritanceUtil;
33 import com.intellij.reference.SoftReference;
34 import com.intellij.util.ConcurrencyUtil;
35 import com.intellij.util.Function;
36 import com.intellij.util.IncorrectOperationException;
37 import com.intellij.util.containers.ConcurrentHashMap;
38 import com.intellij.util.containers.ConcurrentWeakHashMap;
39 import org.jetbrains.annotations.NotNull;
40 import org.jetbrains.annotations.Nullable;
41 import org.jetbrains.plugins.groovy.lang.psi.GroovyPsiElement;
42 import org.jetbrains.plugins.groovy.lang.psi.GroovyPsiElementFactory;
43 import org.jetbrains.plugins.groovy.lang.psi.api.statements.typedef.GrTypeDefinition;
44 import org.jetbrains.plugins.groovy.lang.psi.util.GroovyCommonClassNames;
45
46 import java.util.List;
47 import java.util.Map;
48 import java.util.Set;
49 import java.util.concurrent.ConcurrentMap;
50
51 /**
52  * @author ven
53  */
54 public class GroovyPsiManager {
55   private static final Logger LOG = Logger.getInstance("org.jetbrains.plugins.groovy.lang.psi.impl.GroovyPsiManager");
56   private static final Set<String> ourPopularClasses = Sets.newHashSet(GroovyCommonClassNames.GROOVY_LANG_CLOSURE,
57                                                                        GroovyCommonClassNames.DEFAULT_BASE_CLASS_NAME,
58                                                                        GroovyCommonClassNames.GROOVY_OBJECT_SUPPORT,
59                                                                        CommonClassNames.JAVA_UTIL_LIST,
60                                                                        CommonClassNames.JAVA_UTIL_COLLECTION,
61                                                                        CommonClassNames.JAVA_LANG_STRING);
62   private final Project myProject;
63
64   private volatile GrTypeDefinition myArrayClass;
65
66   private final ConcurrentMap<GroovyPsiElement, PsiType> myCalculatedTypes = new ConcurrentWeakHashMap<GroovyPsiElement, PsiType>();
67   private final ConcurrentMap<String, SoftReference<Map<GlobalSearchScope, PsiClass>>> myClassCache = new ConcurrentHashMap<String, SoftReference<Map<GlobalSearchScope, PsiClass>>>();
68
69   private static final RecursionGuard ourGuard = RecursionManager.createGuard("groovyPsiManager");
70
71   public GroovyPsiManager(Project project) {
72     myProject = project;
73
74     ((PsiManagerEx)PsiManager.getInstance(myProject)).registerRunnableToRunOnAnyChange(new Runnable() {
75       public void run() {
76         dropTypesCache();
77       }
78     });
79     ((PsiManagerEx)PsiManager.getInstance(myProject)).registerRunnableToRunOnChange(new Runnable() {
80       public void run() {
81         myClassCache.clear();
82       }
83     });
84
85     myProject.getMessageBus().connect().subscribe(ProjectTopics.PROJECT_ROOTS, new ModuleRootAdapter() {
86       public void rootsChanged(ModuleRootEvent event) {
87         dropTypesCache();
88         myClassCache.clear();
89       }
90     });
91   }
92
93   public void dropTypesCache() {
94     myCalculatedTypes.clear();
95   }
96
97   public static boolean isInheritorCached(@Nullable PsiClass aClass, @NotNull String baseClassName) {
98     if (aClass == null) return false;
99
100     return InheritanceUtil.isInheritorOrSelf(aClass, getInstance(aClass.getProject()).findClassWithCache(baseClassName, aClass.getResolveScope()), true);
101   }
102
103   public static boolean isInheritorCached(@Nullable PsiType type, @NotNull String baseClassName) {
104     if (type instanceof PsiClassType) {
105       return isInheritorCached(((PsiClassType)type).resolve(), baseClassName);
106     }
107     return false;
108   }
109
110   public static GroovyPsiManager getInstance(Project project) {
111     return ServiceManager.getService(project, GroovyPsiManager.class);
112   }
113
114   public PsiClassType createTypeByFQClassName(String fqName, GlobalSearchScope resolveScope) {
115     if (ourPopularClasses.contains(fqName)) {
116       PsiClass result = findClassWithCache(fqName, resolveScope);
117       if (result != null) {
118         return JavaPsiFacade.getElementFactory(myProject).createType(result);
119       }
120     }
121
122     return JavaPsiFacade.getElementFactory(myProject).createTypeByFQClassName(fqName, resolveScope);
123   }
124
125   @Nullable
126   public PsiClass findClassWithCache(String fqName, GlobalSearchScope resolveScope) {
127     SoftReference<Map<GlobalSearchScope, PsiClass>> reference = myClassCache.get(fqName);
128     Map<GlobalSearchScope, PsiClass> map = reference == null ? null : reference.get();
129     if (map == null) {
130       map = new ConcurrentHashMap<GlobalSearchScope, PsiClass>();
131       myClassCache.put(fqName, new SoftReference<Map<GlobalSearchScope, PsiClass>>(map));
132     }
133     PsiClass cached = map.get(resolveScope);
134     if (cached != null) {
135       return cached;
136     }
137
138     PsiClass result = JavaPsiFacade.getInstance(myProject).findClass(fqName, resolveScope);
139     if (result != null) {
140       map.put(resolveScope, result);
141     }
142     return result;
143   }
144
145   @Nullable
146   public <T extends GroovyPsiElement> PsiType getType(T element, Function<T, PsiType> calculator) {
147     PsiType type = myCalculatedTypes.get(element);
148     if (type == null) {
149       RecursionGuard.StackStamp stamp = ourGuard.markStack();
150       type = calculator.fun(element);
151       if (type == null) {
152         type = PsiType.NULL;
153       }
154       if (stamp.mayCacheNow()) {
155         type = ConcurrencyUtil.cacheOrGet(myCalculatedTypes, element, type);
156       } else {
157         final PsiType alreadyInferred = myCalculatedTypes.get(element);
158         if (alreadyInferred != null) {
159           type = alreadyInferred;
160         }
161       }
162     }
163     if (!type.isValid()) {
164       LOG.error("Type is invalid: " + type + "; element: " + element + " of class " + element.getClass());
165     }
166     return PsiType.NULL.equals(type) ? null : type;
167   }
168
169   public GrTypeDefinition getArrayClass() {
170     if (myArrayClass == null) {
171       try {
172         myArrayClass = GroovyPsiElementFactory.getInstance(myProject).createTypeDefinition("class __ARRAY__ { public int length }");
173       }
174       catch (IncorrectOperationException e) {
175         LOG.error(e);
176       }
177     }
178
179     return myArrayClass;
180   }
181
182   @Nullable
183   public static PsiType inferType(@NotNull PsiElement element, Computable<PsiType> computable) {
184     List<Object> stack = ourGuard.currentStack();
185     if (stack.size() > 7) { //don't end up walking the whole project PSI
186       ourGuard.prohibitResultCaching(stack.get(0));
187       return null;
188     }
189
190     return ourGuard.doPreventingRecursion(element, false, computable);
191   }
192
193 }