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