Cleanup: NotNull/Nullable
[idea/community.git] / java / java-impl / src / com / intellij / psi / impl / source / resolve / reference / impl / providers / JavaClassReference.java
1 /*
2  * Copyright 2000-2018 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.source.resolve.reference.impl.providers;
17
18 import com.intellij.codeInsight.completion.JavaClassNameCompletionContributor;
19 import com.intellij.codeInsight.completion.JavaLookupElementBuilder;
20 import com.intellij.codeInsight.completion.scope.JavaCompletionProcessor;
21 import com.intellij.codeInsight.daemon.impl.quickfix.QuickFixActionRegistrarImpl;
22 import com.intellij.codeInsight.daemon.quickFix.CreateClassOrPackageFix;
23 import com.intellij.codeInsight.intention.QuickFixFactory;
24 import com.intellij.codeInsight.lookup.LookupElement;
25 import com.intellij.codeInsight.lookup.LookupElementBuilder;
26 import com.intellij.codeInspection.LocalQuickFix;
27 import com.intellij.codeInspection.LocalQuickFixProvider;
28 import com.intellij.lang.java.JavaLanguage;
29 import com.intellij.openapi.diagnostic.Logger;
30 import com.intellij.openapi.module.Module;
31 import com.intellij.openapi.module.ModuleUtilCore;
32 import com.intellij.openapi.project.Project;
33 import com.intellij.openapi.util.Iconable;
34 import com.intellij.openapi.util.Key;
35 import com.intellij.openapi.util.TextRange;
36 import com.intellij.openapi.util.text.StringUtil;
37 import com.intellij.psi.*;
38 import com.intellij.psi.impl.file.PsiPackageImpl;
39 import com.intellij.psi.impl.source.resolve.ClassResolverProcessor;
40 import com.intellij.psi.impl.source.resolve.ResolveCache;
41 import com.intellij.psi.impl.source.resolve.reference.impl.GenericReference;
42 import com.intellij.psi.infos.CandidateInfo;
43 import com.intellij.psi.infos.ClassCandidateInfo;
44 import com.intellij.psi.scope.JavaScopeProcessorEvent;
45 import com.intellij.psi.scope.PsiScopeProcessor;
46 import com.intellij.psi.scope.util.PsiScopesUtil;
47 import com.intellij.psi.search.GlobalSearchScope;
48 import com.intellij.psi.search.PackageScope;
49 import com.intellij.psi.search.ProjectScope;
50 import com.intellij.psi.search.searches.ClassInheritorsSearch;
51 import com.intellij.psi.util.ClassKind;
52 import com.intellij.psi.util.ClassUtil;
53 import com.intellij.psi.util.PsiUtil;
54 import com.intellij.util.ArrayUtil;
55 import com.intellij.util.Consumer;
56 import com.intellij.util.IncorrectOperationException;
57 import com.intellij.util.ObjectUtils;
58 import com.intellij.util.containers.ContainerUtil;
59 import com.intellij.util.containers.JBIterable;
60 import com.intellij.util.text.CharArrayUtil;
61 import org.jetbrains.annotations.NotNull;
62 import org.jetbrains.annotations.Nullable;
63
64 import java.util.ArrayList;
65 import java.util.Collections;
66 import java.util.List;
67 import java.util.Map;
68
69 /**
70  * @author peter
71  */
72 public class JavaClassReference extends GenericReference implements PsiJavaReference, LocalQuickFixProvider {
73   private static final Logger LOG = Logger.getInstance("#com.intellij.psi.impl.source.resolve.reference.impl.providers.JavaClassReference");
74   protected final int myIndex;
75   private TextRange myRange;
76   private final String myText;
77   private final boolean myInStaticImport;
78   private final JavaClassReferenceSet myJavaClassReferenceSet;
79
80   public JavaClassReference(final JavaClassReferenceSet referenceSet, TextRange range, int index, String text, final boolean staticImport) {
81     super(referenceSet.getProvider());
82     myInStaticImport = staticImport;
83     LOG.assertTrue(range.getEndOffset() <= referenceSet.getElement().getTextLength());
84     myIndex = index;
85     myRange = range;
86     myText = text;
87     myJavaClassReferenceSet = referenceSet;
88   }
89
90   @Override
91   @Nullable
92   public PsiElement getContext() {
93     final PsiReference contextRef = getContextReference();
94     assert contextRef != this : getCanonicalText();
95     return contextRef != null ? contextRef.resolve() : null;
96   }
97
98   @Override
99   public void processVariants(@NotNull final PsiScopeProcessor processor) {
100     if (processor instanceof JavaCompletionProcessor) {
101       final Map<CustomizableReferenceProvider.CustomizationKey, Object> options = getOptions();
102       if (options != null &&
103           (JavaClassReferenceProvider.SUPER_CLASSES.getValue(options) != null ||
104            JavaClassReferenceProvider.NOT_INTERFACE.getBooleanValue(options) ||
105            JavaClassReferenceProvider.CONCRETE.getBooleanValue(options)) ||
106            JavaClassReferenceProvider.CLASS_KIND.getValue(options) != null) {
107         ((JavaCompletionProcessor)processor).setCompletionElements(getVariants());
108         return;
109       }
110     }
111
112     PsiScopeProcessor processorToUse = processor;
113     if (myInStaticImport) {
114       // allows to complete members
115       processor.handleEvent(JavaScopeProcessorEvent.CHANGE_LEVEL, null);
116     }
117     else {
118       if (isDefinitelyStatic()) {
119         processor.handleEvent(JavaScopeProcessorEvent.START_STATIC, null);
120       }
121       processorToUse = new PsiScopeProcessor() {
122         @Override
123         public boolean execute(@NotNull PsiElement element, @NotNull ResolveState state) {
124           return !(element instanceof PsiClass || element instanceof PsiPackage) || processor.execute(element, state);
125         }
126
127         @Override
128         public <V> V getHint(@NotNull Key<V> hintKey) {
129           return processor.getHint(hintKey);
130         }
131
132         @Override
133         public void handleEvent(@NotNull Event event, Object associated) {
134           processor.handleEvent(event, associated);
135         }
136       };
137     }
138     super.processVariants(processorToUse);
139   }
140
141   private boolean isDefinitelyStatic() {
142     final String s = getElement().getText();
143     return isStaticClassReference(s, true);
144   }
145
146   private boolean isStaticClassReference(final String s, boolean strict) {
147     if (myIndex == 0) {
148       return false;
149     }
150     char c = s.charAt(getRangeInElement().getStartOffset() - 1);
151     return myJavaClassReferenceSet.isStaticSeparator(c, strict);
152   }
153
154   @Override
155   @Nullable
156   public PsiReference getContextReference() {
157     return myIndex > 0 ? myJavaClassReferenceSet.getReference(myIndex - 1) : null;
158   }
159
160   private boolean canReferencePackage() {
161     return myJavaClassReferenceSet.canReferencePackage(myIndex);
162   }
163
164   @NotNull
165   @Override
166   public PsiElement getElement() {
167     return myJavaClassReferenceSet.getElement();
168   }
169
170   @Override
171   public boolean isReferenceTo(@NotNull PsiElement element) {
172     return (element instanceof PsiMember || element instanceof PsiPackage) && super.isReferenceTo(element);
173   }
174
175   @NotNull
176   @Override
177   public TextRange getRangeInElement() {
178     return myRange;
179   }
180
181   @Override
182   @NotNull
183   public String getCanonicalText() {
184     return myText;
185   }
186
187   @Override
188   public boolean isSoft() {
189     return myJavaClassReferenceSet.isSoft();
190   }
191
192   @Override
193   public PsiElement handleElementRename(@NotNull String newElementName) throws IncorrectOperationException {
194     final ElementManipulator<PsiElement> manipulator = getManipulator(getElement());
195     final PsiElement element = manipulator.handleContentChange(getElement(), getRangeInElement(), newElementName);
196     myRange = new TextRange(getRangeInElement().getStartOffset(), getRangeInElement().getStartOffset() + newElementName.length());
197     return element;
198   }
199
200   @Override
201   public PsiElement bindToElement(@NotNull PsiElement element) throws IncorrectOperationException {
202     if (isReferenceTo(element)) return getElement();
203
204     final String newName;
205     if (element instanceof PsiClass) {
206       newName = getQualifiedClassNameToInsert((PsiClass)element);
207     }
208     else if (element instanceof PsiPackage) {
209       PsiPackage psiPackage = (PsiPackage)element;
210       newName = psiPackage.getQualifiedName();
211     }
212     else {
213       throw new IncorrectOperationException("Cannot bind to " + element);
214     }
215     assert newName != null;
216
217     int end = getRangeInElement().getEndOffset();
218     String text = getElement().getText();
219     int lt = text.indexOf('<', getRangeInElement().getStartOffset());
220     if (lt >= 0 && lt < end) {
221       end = CharArrayUtil.shiftBackward(text, lt - 1, "\n\t ") + 1;
222     }
223     TextRange range = new TextRange(myJavaClassReferenceSet.getReference(0).getRangeInElement().getStartOffset(), end);
224     final ElementManipulator<PsiElement> manipulator = getManipulator(getElement());
225     final PsiElement finalElement = manipulator.handleContentChange(getElement(), range, newName);
226     myJavaClassReferenceSet.reparse(finalElement, TextRange.from(range.getStartOffset(), newName.length()));
227     return finalElement;
228   }
229
230   private String getQualifiedClassNameToInsert(PsiClass psiClass) {
231     final boolean jvmFormat = Boolean.TRUE.equals(JavaClassReferenceProvider.JVM_FORMAT.getValue(getOptions()));
232     return jvmFormat ? ClassUtil.getJVMClassName(psiClass) : psiClass.getQualifiedName();
233   }
234
235   @Override
236   public PsiElement resolveInner() {
237     return advancedResolve(true).getElement();
238   }
239
240   @Override
241   @NotNull
242   public Object[] getVariants() {
243     List<Object> result = ContainerUtil.newArrayList();
244     for (PsiElement context : getCompletionContexts()) {
245       if (context instanceof PsiPackage) {
246         result.addAll(processPackage((PsiPackage)context));
247       }
248       else if (context instanceof PsiClass) {
249         if (myInStaticImport) {
250           Collections.addAll(result, ((PsiClass)context).getInnerClasses());
251           Collections.addAll(result, ((PsiClass)context).getFields());
252         }
253         else if (isDefinitelyStatic()) {
254           result.addAll(ContainerUtil.filter(((PsiClass)context).getInnerClasses(), c -> c.hasModifierProperty(PsiModifier.STATIC)));
255         }
256       }
257     }
258     return result.toArray();
259   }
260
261   private List<? extends PsiElement> getCompletionContexts() {
262     List<PsiElement> result = ContainerUtil.newArrayList();
263
264     ContainerUtil.addIfNotNull(result, getCompletionContext());
265
266     List<String> imports = JavaClassReferenceProvider.IMPORTS.getValue(getOptions());
267     if (imports != null && getContextReference() == null) {
268       result.addAll(ContainerUtil.mapNotNull(imports, JavaPsiFacade.getInstance(getElement().getProject())::findPackage));
269     }
270
271     return result;
272   }
273
274   @Nullable
275   public PsiElement getCompletionContext() {
276     PsiElement context = getContext();
277     return context == null ? JavaPsiFacade.getInstance(getElement().getProject()).findPackage("") : context;
278   }
279
280   /** @deprecated use {@link #getSuperClasses()} instead */
281   @Deprecated
282   @Nullable
283   public String[] getExtendClassNames() {
284     List<String> result = getSuperClasses();
285     return result.isEmpty() ? null : ArrayUtil.toStringArray(result);
286   }
287
288   @NotNull
289   public List<String> getSuperClasses() {
290     List<String> values = JavaClassReferenceProvider.SUPER_CLASSES.getValue(getOptions());
291     return values == null ? Collections.emptyList() : values;
292   }
293
294   @NotNull
295   private List<LookupElement> processPackage(@NotNull PsiPackage aPackage) {
296     final ArrayList<LookupElement> list = ContainerUtil.newArrayList();
297     final int startOffset = StringUtil.isEmpty(aPackage.getName()) ? 0 : aPackage.getQualifiedName().length() + 1;
298     final GlobalSearchScope scope = getScope(getJavaContextFile());
299     for (final PsiPackage subPackage : aPackage.getSubPackages(scope)) {
300       final String shortName = subPackage.getQualifiedName().substring(startOffset);
301       if (PsiNameHelper.getInstance(subPackage.getProject()).isIdentifier(shortName)) {
302         list.add(LookupElementBuilder.create(subPackage).withIcon(subPackage.getIcon(Iconable.ICON_FLAG_VISIBILITY)));
303       }
304     }
305
306     final List<PsiClass> classes = ContainerUtil.filter(aPackage.getClasses(scope), psiClass -> StringUtil.isNotEmpty(psiClass.getName()));
307     final Map<CustomizableReferenceProvider.CustomizationKey, Object> options = getOptions();
308     if (options != null) {
309       final boolean instantiatable = JavaClassReferenceProvider.INSTANTIATABLE.getBooleanValue(options);
310       final boolean concrete = JavaClassReferenceProvider.CONCRETE.getBooleanValue(options);
311       final boolean notInterface = JavaClassReferenceProvider.NOT_INTERFACE.getBooleanValue(options);
312       final boolean notEnum = JavaClassReferenceProvider.NOT_ENUM.getBooleanValue(options);
313       final ClassKind classKind = getClassKind();
314
315       for (PsiClass clazz : classes) {
316         if (isClassAccepted(clazz, classKind, instantiatable, concrete, notInterface, notEnum)) {
317           list.add(JavaClassNameCompletionContributor.createClassLookupItem(clazz, false));
318         }
319       }
320     }
321     else {
322       for (PsiClass clazz : classes) {
323         list.add(JavaClassNameCompletionContributor.createClassLookupItem(clazz, false));
324       }
325     }
326     return list;
327   }
328
329   @Nullable
330   public ClassKind getClassKind() {
331     return JavaClassReferenceProvider.CLASS_KIND.getValue(getOptions());
332   }
333
334   private static boolean isClassAccepted(final PsiClass clazz,
335                                          @Nullable final ClassKind classKind,
336                                          final boolean instantiatable,
337                                          final boolean concrete,
338                                          final boolean notInterface,
339                                          final boolean notEnum) {
340     if (classKind == ClassKind.ANNOTATION)  return clazz.isAnnotationType();
341     if (classKind == ClassKind.ENUM) return clazz.isEnum();
342
343     if (instantiatable) {
344       if (PsiUtil.isInstantiatable(clazz)) {
345         return true;
346       }
347     }
348     else if (concrete) {
349       if (!clazz.hasModifierProperty(PsiModifier.ABSTRACT) && !clazz.isInterface()) {
350         return true;
351       }
352     }
353     else if (notInterface) {
354       if (!clazz.isInterface()) {
355         return true;
356       }
357     }
358     else if (notEnum) {
359       if (!clazz.isEnum()) {
360         return true;
361       }
362     }
363     else {
364       return true;
365     }
366     return false;
367   }
368
369   @Override
370   @NotNull
371   public JavaResolveResult advancedResolve(boolean incompleteCode) {
372     PsiFile file = getJavaContextFile();
373     final ResolveCache resolveCache = ResolveCache.getInstance(file.getProject());
374     return (JavaResolveResult) resolveCache.resolveWithCaching(this, MyResolver.INSTANCE, false, false,file)[0];
375   }
376
377   private PsiFile getJavaContextFile() {
378     return myJavaClassReferenceSet.getProvider().getContextFile(getElement());
379   }
380
381   @NotNull
382   private JavaResolveResult doAdvancedResolve(@NotNull PsiFile containingFile) {
383     final PsiElement psiElement = getElement();
384
385     if (!psiElement.isValid()) return JavaResolveResult.EMPTY;
386
387     final String elementText = psiElement.getText();
388
389     final PsiElement context = getContext();
390     if (context instanceof PsiClass) {
391       if (isStaticClassReference(elementText, false)) {
392         final PsiClass psiClass = ((PsiClass)context).findInnerClassByName(getCanonicalText(), false);
393         if (psiClass != null) {
394           return new ClassCandidateInfo(psiClass, PsiSubstitutor.EMPTY, false, psiElement);
395         }
396         PsiElement member = doResolveMember((PsiClass)context, myText);
397         return member == null ? JavaResolveResult.EMPTY : new CandidateInfo(member, PsiSubstitutor.EMPTY, false, false, psiElement);
398       }
399       else if (!myInStaticImport && myJavaClassReferenceSet.isAllowDollarInNames()) {
400         return JavaResolveResult.EMPTY;
401       }
402     }
403
404     final int endOffset = getRangeInElement().getEndOffset();
405     LOG.assertTrue(endOffset <= elementText.length(), elementText);
406     final int startOffset = myJavaClassReferenceSet.getReference(0).getRangeInElement().getStartOffset();
407     final String qName = elementText.substring(startOffset, endOffset);
408     if (!qName.contains(".")) {
409       JavaResolveResult viaImports = JBIterable.from(JavaClassReferenceProvider.IMPORTS.getValue(getOptions()))
410         .map(o -> o == null ? JavaResolveResult.EMPTY : advancedResolveInner(psiElement, o + "." + qName, containingFile))
411         .find(o -> o != JavaResolveResult.EMPTY);
412       if (viaImports != null) {
413         return viaImports;
414       }
415     }
416     return advancedResolveInner(psiElement, qName, containingFile);
417   }
418
419   private JavaResolveResult advancedResolveInner(@NotNull PsiElement psiElement, @NotNull String qName, @NotNull PsiFile containingFile) {
420     final PsiManager manager = containingFile.getManager();
421     final GlobalSearchScope scope = getScope(containingFile);
422     if (myIndex == myJavaClassReferenceSet.getReferences().length - 1) {
423       final PsiClass aClass = JavaPsiFacade.getInstance(manager.getProject()).findClass(qName, scope);
424       if (aClass != null) {
425         return new ClassCandidateInfo(aClass, PsiSubstitutor.EMPTY, false, psiElement);
426       }
427       else {
428         if (!JavaClassReferenceProvider.ADVANCED_RESOLVE.getBooleanValue(getOptions())) {
429           return JavaResolveResult.EMPTY;
430         }
431       }
432     }
433     PsiElement resolveResult = JavaPsiFacade.getInstance(manager.getProject()).findPackage(qName);
434     if (resolveResult == null) {
435       resolveResult = JavaPsiFacade.getInstance(manager.getProject()).findClass(qName, scope);
436     }
437     if (myInStaticImport && resolveResult == null) {
438       resolveResult = resolveMember(qName, manager, getElement().getResolveScope());
439     }
440     if (resolveResult == null) {
441       if (containingFile instanceof PsiJavaFile) {
442         if (containingFile instanceof ServerPageFile) {
443           containingFile = containingFile.getViewProvider().getPsi(JavaLanguage.INSTANCE);
444           if (containingFile == null) return JavaResolveResult.EMPTY;
445         }
446
447         final ClassResolverProcessor processor = new ClassResolverProcessor(getCanonicalText(), psiElement, containingFile);
448         PsiClass contextClass = myJavaClassReferenceSet.getProvider().getContextClass(psiElement);
449         if (contextClass != null) {
450           PsiScopesUtil.treeWalkUp(processor, contextClass, null);
451         }
452         else {
453           containingFile.processDeclarations(processor, ResolveState.initial(), null, psiElement);
454         }
455
456         if (processor.getResult().length == 1) {
457           final JavaResolveResult javaResolveResult = processor.getResult()[0];
458
459           if (javaResolveResult != JavaResolveResult.EMPTY && getOptions() != null) {
460             final Boolean value = JavaClassReferenceProvider.RESOLVE_QUALIFIED_CLASS_NAME.getValue(getOptions());
461             final PsiClass psiClass = (PsiClass)javaResolveResult.getElement();
462             if (value != null && value.booleanValue() && psiClass != null) {
463               final String qualifiedName = psiClass.getQualifiedName();
464
465               if (!qName.equals(qualifiedName)) {
466                 return JavaResolveResult.EMPTY;
467               }
468             }
469           }
470
471           return javaResolveResult;
472         }
473       }
474     }
475     if (resolveResult instanceof PsiPackageImpl && !((PsiPackageImpl)resolveResult).mayHaveContentInScope(scope)) {
476       return JavaResolveResult.EMPTY;
477     }
478     return resolveResult != null
479            ? new CandidateInfo(resolveResult, PsiSubstitutor.EMPTY, false, false, psiElement)
480            : JavaResolveResult.EMPTY;
481   }
482
483   private GlobalSearchScope getScope(@NotNull PsiFile containingFile) {
484     Project project = containingFile.getProject();
485     GlobalSearchScope scope = myJavaClassReferenceSet.getProvider().getScope(project);
486     if (scope == null) {
487       Module module = ModuleUtilCore.findModuleForPsiElement(containingFile);
488       return module == null ? GlobalSearchScope.allScope(project) : module.getModuleWithDependenciesAndLibrariesScope(true);
489     }
490     return scope;
491   }
492
493   @Nullable
494   private Map<CustomizableReferenceProvider.CustomizationKey, Object> getOptions() {
495     return myJavaClassReferenceSet.getOptions();
496   }
497
498   @Override
499   @NotNull
500   public JavaResolveResult[] multiResolve(boolean incompleteCode) {
501     final JavaResolveResult javaResolveResult = advancedResolve(incompleteCode);
502     if (javaResolveResult.getElement() == null) return JavaResolveResult.EMPTY_ARRAY;
503     return new JavaResolveResult[]{javaResolveResult};
504   }
505
506   @Nullable
507   private List<? extends LocalQuickFix> registerFixes() {
508     final List<LocalQuickFix> list = QuickFixFactory.getInstance().registerOrderEntryFixes(new QuickFixActionRegistrarImpl(null), this);
509
510     final String[] extendClasses = getExtendClassNames();
511     final String extendClass = extendClasses != null && extendClasses.length > 0 ? extendClasses[0] : null;
512
513     final JavaClassReference[] references = getJavaClassReferenceSet().getAllReferences();
514     PsiPackage contextPackage = null;
515     for (int i = myIndex; i >= 0; i--) {
516       final PsiElement context = references[i].getContext();
517       if (context != null) {
518         if (context instanceof PsiPackage) {
519           contextPackage = (PsiPackage)context;
520         }
521         break;
522       }
523     }
524
525     boolean createJavaClass = !canReferencePackage();
526     ClassKind kind = createJavaClass ? getClassKind() : null;
527     if (createJavaClass && kind == null) kind = ClassKind.CLASS;
528     final String templateName = JavaClassReferenceProvider.CLASS_TEMPLATE.getValue(getOptions());
529     final TextRange range = new TextRange(references[0].getRangeInElement().getStartOffset(),
530                                           getRangeInElement().getEndOffset());
531     final String qualifiedName = range.substring(getElement().getText());
532     final CreateClassOrPackageFix action = CreateClassOrPackageFix.createFix(qualifiedName, getScope(getJavaContextFile()), getElement(), contextPackage,
533                                                                              kind, extendClass, templateName);
534     if (action != null) {
535       if (list == null) {
536         return Collections.singletonList(action);
537       }
538       else {
539         final ArrayList<LocalQuickFix> fixes = new ArrayList<>(list.size() + 1);
540         fixes.addAll(list);
541         fixes.add(action);
542         return fixes;
543       }
544     }
545     return list;
546   }
547
548   public void processSubclassVariants(@NotNull PsiPackage context, @NotNull String[] extendClasses, Consumer<LookupElement> result) {
549     GlobalSearchScope packageScope = PackageScope.packageScope(context, true);
550     GlobalSearchScope scope = myJavaClassReferenceSet.getProvider().getScope(getElement().getProject());
551     if (scope != null) {
552       packageScope = packageScope.intersectWith(scope);
553     }
554     final GlobalSearchScope allScope = ProjectScope.getAllScope(context.getProject());
555     final boolean instantiatable = JavaClassReferenceProvider.INSTANTIATABLE.getBooleanValue(getOptions());
556     final boolean notInterface = JavaClassReferenceProvider.NOT_INTERFACE.getBooleanValue(getOptions());
557     final boolean notEnum = JavaClassReferenceProvider.NOT_ENUM.getBooleanValue(getOptions());
558     final boolean concrete = JavaClassReferenceProvider.CONCRETE.getBooleanValue(getOptions());
559
560     final ClassKind classKind = getClassKind();
561
562     for (String extendClassName : extendClasses) {
563       final PsiClass extendClass = JavaPsiFacade.getInstance(context.getProject()).findClass(extendClassName, allScope);
564       if (extendClass != null) {
565         // add itself
566         if (packageScope.contains(extendClass.getContainingFile().getVirtualFile())) {
567           if (isClassAccepted(extendClass, classKind, instantiatable, concrete, notInterface, notEnum)) {
568             result.consume(createSubclassLookupValue(extendClass));
569           }
570         }
571         for (final PsiClass clazz : ClassInheritorsSearch.search(extendClass, packageScope, true)) {
572           String qname = clazz.getQualifiedName();
573           if (qname != null && isClassAccepted(clazz, classKind, instantiatable, concrete, notInterface, notEnum)) {
574             result.consume(createSubclassLookupValue(clazz));
575           }
576         }
577       }
578     }
579   }
580
581   @NotNull
582   private LookupElementBuilder createSubclassLookupValue(@NotNull PsiClass clazz) {
583     return JavaLookupElementBuilder.forClass(clazz, getQualifiedClassNameToInsert(clazz), true)
584       .withPresentableText(ObjectUtils.assertNotNull(clazz.getName()));
585   }
586
587   @Override
588   public LocalQuickFix[] getQuickFixes() {
589     final List<? extends LocalQuickFix> list = registerFixes();
590     return list == null ? LocalQuickFix.EMPTY_ARRAY : list.toArray(LocalQuickFix.EMPTY_ARRAY);
591   }
592
593   @Nullable
594   public static PsiElement resolveMember(String fqn, PsiManager manager, GlobalSearchScope resolveScope) {
595     PsiClass aClass = JavaPsiFacade.getInstance(manager.getProject()).findClass(fqn, resolveScope);
596     if (aClass != null) return aClass;
597     int i = fqn.lastIndexOf('.');
598     if (i == -1) return null;
599     String memberName = fqn.substring(i + 1);
600     fqn = fqn.substring(0, i);
601     aClass = JavaPsiFacade.getInstance(manager.getProject()).findClass(fqn, resolveScope);
602     if (aClass == null) return null;
603     return doResolveMember(aClass, memberName);
604   }
605
606   @Nullable
607   private static PsiElement doResolveMember(PsiClass aClass, String memberName) {
608     PsiMember member = aClass.findFieldByName(memberName, true);
609     if (member != null) return member;
610
611     PsiMethod[] methods = aClass.findMethodsByName(memberName, true);
612     return methods.length == 0 ? null : methods[0];
613   }
614
615   public JavaClassReferenceSet getJavaClassReferenceSet() {
616     return myJavaClassReferenceSet;
617   }
618
619   @NotNull
620   @Override
621   public String getUnresolvedMessagePattern() {
622     return myJavaClassReferenceSet.getUnresolvedMessagePattern(myIndex);
623   }
624
625   private static class MyResolver implements ResolveCache.PolyVariantContextResolver<JavaClassReference> {
626     private static final MyResolver INSTANCE = new MyResolver();
627
628     @NotNull
629     @Override
630     public ResolveResult[] resolve(@NotNull JavaClassReference ref, @NotNull PsiFile containingFile, boolean incompleteCode) {
631       return new JavaResolveResult[]{ref.doAdvancedResolve(containingFile)};
632     }
633   }
634
635   @Override
636   public String toString() {
637     return getClass().getName() + "(" + getRangeInElement() + ", provider=" + myJavaClassReferenceSet.getProvider() + "}";
638   }
639 }