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