Cleanup: NotNull/Nullable
[idea/community.git] / java / java-indexing-impl / src / com / intellij / psi / impl / PsiShortNamesCacheImpl.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.intellij.psi.impl;
17
18 import com.intellij.openapi.progress.ProgressIndicatorProvider;
19 import com.intellij.openapi.project.Project;
20 import com.intellij.openapi.vfs.VirtualFile;
21 import com.intellij.psi.*;
22 import com.intellij.psi.impl.java.stubs.index.JavaFieldNameIndex;
23 import com.intellij.psi.impl.java.stubs.index.JavaMethodNameIndex;
24 import com.intellij.psi.impl.java.stubs.index.JavaShortClassNameIndex;
25 import com.intellij.psi.impl.java.stubs.index.JavaStubIndexKeys;
26 import com.intellij.psi.impl.search.JavaSourceFilterScope;
27 import com.intellij.psi.search.FilenameIndex;
28 import com.intellij.psi.search.GlobalSearchScope;
29 import com.intellij.psi.search.PsiShortNamesCache;
30 import com.intellij.psi.stubs.StubIndex;
31 import com.intellij.util.ArrayUtil;
32 import com.intellij.util.CommonProcessors;
33 import com.intellij.util.Processor;
34 import com.intellij.util.SmartList;
35 import com.intellij.util.indexing.IdFilter;
36 import gnu.trove.THashMap;
37 import gnu.trove.THashSet;
38 import gnu.trove.TObjectHashingStrategy;
39 import org.jetbrains.annotations.NotNull;
40 import org.jetbrains.annotations.Nullable;
41
42 import java.util.*;
43
44 public class PsiShortNamesCacheImpl extends PsiShortNamesCache {
45   private final Project myProject;
46
47   public PsiShortNamesCacheImpl(Project project) {
48     myProject = project;
49   }
50
51   @Override
52   @NotNull
53   public PsiFile[] getFilesByName(@NotNull String name) {
54     return FilenameIndex.getFilesByName(myProject, name, GlobalSearchScope.projectScope(myProject));
55   }
56
57   @Override
58   @NotNull
59   public String[] getAllFileNames() {
60     return FilenameIndex.getAllFilenames(myProject);
61   }
62
63   @Override
64   @NotNull
65   public PsiClass[] getClassesByName(@NotNull String name, @NotNull GlobalSearchScope scope) {
66     Collection<PsiClass> classes = JavaShortClassNameIndex.getInstance().get(name, myProject, scope);
67     if (classes.isEmpty()) return PsiClass.EMPTY_ARRAY;
68
69     List<PsiClass> result = new ArrayList<>(classes.size());
70     Map<String, List<PsiClass>> uniqueQName2Classes = new THashMap<>(classes.size());
71     Set<PsiClass> hiddenClassesToRemove = null;
72
73     OuterLoop:
74     for (PsiClass aClass : classes) {
75       VirtualFile vFile = aClass.getContainingFile().getVirtualFile();
76       if (!scope.contains(vFile)) continue;
77
78       String qName = aClass.getQualifiedName();
79       if (qName != null) {
80         List<PsiClass> previousQNamedClasses = uniqueQName2Classes.get(qName);
81         List<PsiClass> qNamedClasses = new SmartList<>();
82
83         if (previousQNamedClasses != null) {
84           for (PsiClass previousClass : previousQNamedClasses) {
85             VirtualFile previousClassVFile = previousClass.getContainingFile().getVirtualFile();
86             int res = scope.compare(previousClassVFile, vFile);
87             if (res > 0) {
88               continue OuterLoop; // previousClass hides aClass in classpath, so skip adding aClass
89             }
90             else if (res < 0) {
91               // aClass hides previousClass in classpath, so remove it from list later
92               if (hiddenClassesToRemove == null) hiddenClassesToRemove = new THashSet<>();
93               hiddenClassesToRemove.add(previousClass);
94               qNamedClasses.add(aClass);
95             }
96             else {
97               qNamedClasses.add(aClass);
98             }
99           }
100         }
101         else {
102           qNamedClasses.add(aClass);
103         }
104
105         uniqueQName2Classes.put(qName, qNamedClasses);
106       }
107
108       result.add(aClass);
109     }
110
111     if (hiddenClassesToRemove != null) result.removeAll(hiddenClassesToRemove);
112
113     return result.toArray(PsiClass.EMPTY_ARRAY);
114   }
115
116   @Override
117   @NotNull
118   public String[] getAllClassNames() {
119     return ArrayUtil.toStringArray(JavaShortClassNameIndex.getInstance().getAllKeys(myProject));
120   }
121
122   @Override
123   public boolean processAllClassNames(@NotNull Processor<String> processor) {
124     return JavaShortClassNameIndex.getInstance().processAllKeys(myProject, processor);
125   }
126
127   @Override
128   public boolean processAllClassNames(@NotNull Processor<String> processor, @NotNull GlobalSearchScope scope, IdFilter filter) {
129     return StubIndex.getInstance().processAllKeys(JavaStubIndexKeys.CLASS_SHORT_NAMES, processor, scope, filter);
130   }
131
132   @Override
133   public boolean processAllMethodNames(@NotNull Processor<String> processor, @NotNull GlobalSearchScope scope, IdFilter filter) {
134     return StubIndex.getInstance().processAllKeys(JavaStubIndexKeys.METHODS, processor, scope, filter);
135   }
136
137   @Override
138   public boolean processAllFieldNames(@NotNull Processor<String> processor, @NotNull GlobalSearchScope scope, IdFilter filter) {
139     return StubIndex.getInstance().processAllKeys(JavaStubIndexKeys.FIELDS, processor, scope, filter);
140   }
141
142   @Override
143   @NotNull
144   public PsiMethod[] getMethodsByName(@NotNull String name, @NotNull GlobalSearchScope scope) {
145     Collection<PsiMethod> methods = JavaMethodNameIndex.getInstance().get(name, myProject, scope);
146     return filterMembers(methods, scope, PsiMethod.EMPTY_ARRAY);
147   }
148
149   @Override
150   @NotNull
151   public PsiMethod[] getMethodsByNameIfNotMoreThan(@NotNull String name, @NotNull GlobalSearchScope scope, int maxCount) {
152     List<PsiMethod> methods = new SmartList<>();
153     Processor<PsiMethod> processor = new CommonProcessors.CollectProcessor<PsiMethod>(methods) {
154       @Override
155       public boolean process(PsiMethod method) {
156         return methods.size() != maxCount && super.process(method);
157       }
158     };
159     StubIndex.getInstance().processElements(JavaStubIndexKeys.METHODS, name, myProject, scope, PsiMethod.class, processor);
160     return filterMembers(methods, scope, PsiMethod.EMPTY_ARRAY);
161   }
162
163   @Override
164   public boolean processMethodsWithName(@NotNull String name, @NotNull GlobalSearchScope scope, @NotNull Processor<PsiMethod> processor) {
165     return StubIndex.getInstance().processElements(JavaStubIndexKeys.METHODS, name, myProject, scope, PsiMethod.class, processor);
166   }
167
168   @Override
169   @NotNull
170   public String[] getAllMethodNames() {
171     return ArrayUtil.toStringArray(JavaMethodNameIndex.getInstance().getAllKeys(myProject));
172   }
173
174   @Override
175   @NotNull
176   public PsiField[] getFieldsByNameIfNotMoreThan(@NotNull String name, @NotNull GlobalSearchScope scope, int maxCount) {
177     List<PsiField> fields = new SmartList<>();
178     Processor<PsiField> processor = new CommonProcessors.CollectProcessor<PsiField>(fields) {
179       @Override
180       public boolean process(PsiField method) {
181         return fields.size() != maxCount && super.process(method);
182       }
183     };
184     StubIndex.getInstance().processElements(JavaStubIndexKeys.FIELDS, name, myProject, scope, PsiField.class, processor);
185     return filterMembers(fields, scope, PsiField.EMPTY_ARRAY);
186   }
187
188   @NotNull
189   @Override
190   public PsiField[] getFieldsByName(@NotNull String name, @NotNull GlobalSearchScope scope) {
191     Collection<PsiField> fields = JavaFieldNameIndex.getInstance().get(name, myProject, scope);
192     return filterMembers(fields, scope, PsiField.EMPTY_ARRAY);
193   }
194
195   @Override
196   @NotNull
197   public String[] getAllFieldNames() {
198     return ArrayUtil.toStringArray(JavaFieldNameIndex.getInstance().getAllKeys(myProject));
199   }
200
201   @Override
202   public boolean processFieldsWithName(@NotNull String name,
203                                        @NotNull Processor<? super PsiField> processor,
204                                        @NotNull GlobalSearchScope scope,
205                                        @Nullable IdFilter filter) {
206     return StubIndex.getInstance().processElements(
207       JavaStubIndexKeys.FIELDS, name, myProject, new JavaSourceFilterScope(scope), filter, PsiField.class, processor);
208   }
209
210   @Override
211   public boolean processMethodsWithName(@NotNull String name,
212                                         @NotNull Processor<? super PsiMethod> processor,
213                                         @NotNull GlobalSearchScope scope,
214                                         @Nullable IdFilter filter) {
215     return StubIndex.getInstance().processElements(
216       JavaStubIndexKeys.METHODS, name, myProject, new JavaSourceFilterScope(scope), filter, PsiMethod.class, processor);
217   }
218
219   @Override
220   public boolean processClassesWithName(@NotNull String name,
221                                         @NotNull Processor<? super PsiClass> processor,
222                                         @NotNull GlobalSearchScope scope,
223                                         @Nullable IdFilter filter) {
224     return StubIndex.getInstance().processElements(
225       JavaStubIndexKeys.CLASS_SHORT_NAMES, name, myProject, new JavaSourceFilterScope(scope), filter, PsiClass.class, processor);
226   }
227
228   @NotNull
229   private <T extends PsiMember> T[] filterMembers(@NotNull Collection<T> members, @NotNull GlobalSearchScope scope, @NotNull T[] emptyArray) {
230     if (members.isEmpty()) {
231       return emptyArray;
232     }
233
234     PsiManager myManager = PsiManager.getInstance(myProject);
235     Set<PsiMember> set = new THashSet<>(members.size(), new TObjectHashingStrategy<PsiMember>() {
236       @Override
237       public int computeHashCode(PsiMember member) {
238         int code = 0;
239         final PsiClass clazz = member.getContainingClass();
240         if (clazz != null) {
241           String name = clazz.getName();
242           if (name != null) {
243             code += name.hashCode();
244           }
245           else {
246             //anonymous classes are not equivalent
247             code += clazz.hashCode();
248           }
249         }
250         if (member instanceof PsiMethod) {
251           code += 37 * ((PsiMethod)member).getParameterList().getParametersCount();
252         }
253         return code;
254       }
255
256       @Override
257       public boolean equals(PsiMember object, PsiMember object1) {
258         return myManager.areElementsEquivalent(object, object1);
259       }
260     });
261
262     List<T> result = new ArrayList<>(members.size());
263     for (T member : members) {
264       ProgressIndicatorProvider.checkCanceled();
265       if (scope.contains(member.getContainingFile().getVirtualFile()) && set.add(member)) {
266         result.add(member);
267       }
268     }
269     return result.toArray(emptyArray);
270   }
271 }