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