IDEA-79862 Java: auto-insert space after completing "extends" and "implements"
[idea/community.git] / java / java-impl / src / com / intellij / codeInsight / completion / JavaSmartCompletionContributor.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.codeInsight.completion;
17
18 import com.intellij.codeInsight.*;
19 import com.intellij.codeInsight.lookup.*;
20 import com.intellij.openapi.util.Key;
21 import com.intellij.openapi.util.Pair;
22 import com.intellij.patterns.ElementPattern;
23 import com.intellij.patterns.PsiJavaPatterns;
24 import com.intellij.psi.*;
25 import com.intellij.psi.filters.ElementExtractorFilter;
26 import com.intellij.psi.filters.ElementFilter;
27 import com.intellij.psi.filters.GeneratorFilter;
28 import com.intellij.psi.filters.OrFilter;
29 import com.intellij.psi.filters.getters.*;
30 import com.intellij.psi.filters.types.AssignableFromFilter;
31 import com.intellij.psi.filters.types.AssignableGroupFilter;
32 import com.intellij.psi.filters.types.AssignableToFilter;
33 import com.intellij.psi.impl.source.resolve.reference.impl.PsiMultiReference;
34 import com.intellij.psi.infos.CandidateInfo;
35 import com.intellij.psi.javadoc.PsiDocTag;
36 import com.intellij.psi.util.InheritanceUtil;
37 import com.intellij.psi.util.PsiTreeUtil;
38 import com.intellij.psi.util.PsiUtil;
39 import com.intellij.psi.util.TypeConversionUtil;
40 import com.intellij.util.Consumer;
41 import com.intellij.util.ProcessingContext;
42 import com.intellij.util.ReflectionCache;
43 import com.intellij.util.SmartList;
44 import com.intellij.util.containers.ContainerUtil;
45 import gnu.trove.THashSet;
46 import gnu.trove.TObjectHashingStrategy;
47 import org.jetbrains.annotations.NonNls;
48 import org.jetbrains.annotations.NotNull;
49 import org.jetbrains.annotations.Nullable;
50
51 import java.util.*;
52
53 import static com.intellij.patterns.PlatformPatterns.psiElement;
54 import static com.intellij.patterns.PsiJavaPatterns.psiMethod;
55 import static com.intellij.patterns.StandardPatterns.*;
56
57 /**
58  * @author peter
59  */
60 public class JavaSmartCompletionContributor extends CompletionContributor {
61   private static final TObjectHashingStrategy<ExpectedTypeInfo> EXPECTED_TYPE_INFO_STRATEGY = new TObjectHashingStrategy<ExpectedTypeInfo>() {
62     public int computeHashCode(final ExpectedTypeInfo object) {
63       return object.getType().hashCode();
64     }
65
66     public boolean equals(final ExpectedTypeInfo o1, final ExpectedTypeInfo o2) {
67       return o1.getType().equals(o2.getType());
68     }
69   };
70
71   private static final ElementExtractorFilter THROWABLES_FILTER = new ElementExtractorFilter(new AssignableFromFilter(CommonClassNames.JAVA_LANG_THROWABLE));
72   @NonNls private static final String EXCEPTION_TAG = "exception";
73   static final ElementPattern<PsiElement> AFTER_NEW =
74       psiElement().afterLeaf(
75           psiElement().withText(PsiKeyword.NEW).andNot(
76               psiElement().afterLeaf(
77                   psiElement().withText(PsiKeyword.THROW))));
78   static final ElementPattern<PsiElement> AFTER_THROW_NEW = psiElement().afterLeaf(psiElement().withText(PsiKeyword.NEW).afterLeaf(PsiKeyword.THROW));
79   private static final OrFilter THROWABLE_TYPE_FILTER = new OrFilter(
80       new GeneratorFilter(AssignableGroupFilter.class, new ThrowsListGetter()),
81       new AssignableFromFilter(CommonClassNames.JAVA_LANG_THROWABLE));
82   public static final ElementPattern<PsiElement> INSIDE_EXPRESSION = or(
83         psiElement().withParent(PsiExpression.class).andNot(psiElement().withParent(PsiLiteralExpression.class)),
84         psiElement().inside(PsiClassObjectAccessExpression.class),
85         psiElement().inside(PsiThisExpression.class),
86         psiElement().inside(PsiSuperExpression.class)
87         );
88   static final ElementPattern<PsiElement> INSIDE_TYPECAST_EXPRESSION = psiElement().withParent(
89     psiElement(PsiReferenceExpression.class).afterLeaf(
90       psiElement().withText(")").withParent(PsiTypeCastExpression.class)));
91
92   @Nullable
93   private static ElementFilter getReferenceFilter(PsiElement element) {
94     //throw new foo
95     if (AFTER_THROW_NEW.accepts(element)) {
96       return new ElementExtractorFilter(THROWABLE_TYPE_FILTER);
97     }
98
99     //new xxx.yyy
100     if (psiElement().afterLeaf(psiElement().withText(".")).withSuperParent(2, psiElement(PsiNewExpression.class)).accepts(element)) {
101       if (((PsiNewExpression)element.getParent().getParent()).getClassReference() == element.getParent()) {
102         return new GeneratorFilter(AssignableGroupFilter.class, new ExpectedTypesGetter());
103       }
104     }
105
106     return null;
107   }
108
109
110
111   public JavaSmartCompletionContributor() {
112     extend(CompletionType.SMART, SmartCastProvider.INSIDE_TYPECAST_TYPE, new SmartCastProvider());
113
114     extend(CompletionType.SMART,
115            psiElement().beforeLeaf(psiElement(JavaTokenType.RPARENTH)).afterLeaf("(").withParent(
116              psiElement(PsiReferenceExpression.class).withParent(
117                psiElement(PsiExpressionList.class).withParent(PsiCall.class))), new SameSignatureCallParametersProvider());
118
119     extend(CompletionType.SMART, psiElement().afterLeaf(PsiKeyword.INSTANCEOF), new CompletionProvider<CompletionParameters>() {
120       protected void addCompletions(@NotNull final CompletionParameters parameters, final ProcessingContext context, @NotNull final CompletionResultSet result) {
121         final PsiElement position = parameters.getPosition();
122         final PsiType[] leftTypes = InstanceOfLeftPartTypeGetter.getLeftTypes(position);
123         final Set<PsiClassType> expectedClassTypes = new LinkedHashSet<PsiClassType>();
124         final Set<PsiClass> parameterizedTypes = new THashSet<PsiClass>();
125         for (final PsiType type : leftTypes) {
126           if (type instanceof PsiClassType) {
127             final PsiClassType classType = (PsiClassType)type;
128             if (!classType.isRaw()) {
129               ContainerUtil.addIfNotNull(classType.resolve(), parameterizedTypes);
130             }
131
132             expectedClassTypes.add(classType.rawType());
133           }
134         }
135
136         JavaInheritorsGetter
137           .processInheritors(parameters, expectedClassTypes, result.getPrefixMatcher(), new Consumer<PsiType>() {
138             public void consume(PsiType type) {
139               final PsiClass psiClass = PsiUtil.resolveClassInType(type);
140               if (psiClass == null) return;
141
142               if (expectedClassTypes.contains(type)) return;
143
144               result.addElement(createInstanceofLookupElement(psiClass, parameterizedTypes));
145             }
146           });
147       }
148     });
149
150     extend(CompletionType.SMART, psiElement(), new CompletionProvider<CompletionParameters>() {
151       protected void addCompletions(@NotNull final CompletionParameters parameters, final ProcessingContext context, @NotNull final CompletionResultSet result) {
152         final PsiElement element = parameters.getPosition();
153         final PsiReference reference = element.getContainingFile().findReferenceAt(parameters.getOffset());
154         if (reference != null) {
155           final ElementFilter filter = getReferenceFilter(element);
156           if (filter != null) {
157             final List<ExpectedTypeInfo> infos = Arrays.asList(getExpectedTypes(parameters));
158             for (final LookupElement item : completeReference(element, reference, filter, true, parameters)) {
159               if (item.getObject() instanceof PsiClass) {
160                 result.addElement(decorate(LookupElementDecorator.withInsertHandler((LookupItem)item, ConstructorInsertHandler.SMART_INSTANCE), infos));
161               }
162             }
163           }
164           else if (INSIDE_TYPECAST_EXPRESSION.accepts(element)) {
165             for (final LookupElement item : completeReference(element, reference, new GeneratorFilter(AssignableToFilter.class, new CastTypeGetter()), false, parameters)) {
166               result.addElement(item);
167             }
168           }
169
170         }
171       }
172     });
173
174     //method throws clause
175     extend(CompletionType.SMART, psiElement().inside(
176       psiElement(PsiReferenceList.class).save("refList").withParent(
177         psiMethod().withThrowsList(get("refList")))), new CompletionProvider<CompletionParameters>() {
178       @Override
179       protected void addCompletions(@NotNull CompletionParameters parameters,
180                                     ProcessingContext context,
181                                     @NotNull CompletionResultSet result) {
182         final PsiElement element = parameters.getPosition();
183         final PsiReference reference = element.getContainingFile().findReferenceAt(parameters.getOffset());
184         assert reference != null;
185         for (final LookupElement item : completeReference(element, reference, THROWABLES_FILTER, true, parameters)) {
186           result.addElement(item);
187         }
188       }
189     });
190
191     extend(CompletionType.SMART, INSIDE_EXPRESSION, new ExpectedTypeBasedCompletionProvider() {
192       protected void addCompletions(final CompletionParameters params, final CompletionResultSet result, final Collection<ExpectedTypeInfo> _infos) {
193         Consumer<LookupElement> noTypeCheck = new Consumer<LookupElement>() {
194           public void consume(final LookupElement lookupElement) {
195             result.addElement(decorate(lookupElement, _infos));
196           }
197         };
198
199         THashSet<ExpectedTypeInfo> mergedInfos = new THashSet<ExpectedTypeInfo>(_infos, EXPECTED_TYPE_INFO_STRATEGY);
200         List<Runnable> chainedEtc = new ArrayList<Runnable>();
201         for (final ExpectedTypeInfo info : mergedInfos) {
202           Runnable slowContinuation =
203             ReferenceExpressionCompletionContributor.fillCompletionVariants(new JavaSmartCompletionParameters(params, info), noTypeCheck);
204           ContainerUtil.addIfNotNull(chainedEtc, slowContinuation);
205         }
206         addExpectedTypeMembers(params, mergedInfos, true, noTypeCheck);
207
208         for (final ExpectedTypeInfo info : mergedInfos) {
209           BasicExpressionCompletionContributor.fillCompletionVariants(new JavaSmartCompletionParameters(params, info), new Consumer<LookupElement>() {
210             @Override
211             public void consume(LookupElement lookupElement) {
212               final TypedLookupItem typed = lookupElement.as(TypedLookupItem.CLASS_CONDITION_KEY);
213               if (typed != null) {
214                 final PsiType psiType = typed.getType();
215                 if (psiType != null && info.getType().isAssignableFrom(psiType)) {
216                   result.addElement(decorate(lookupElement, _infos));
217                 }
218               }
219             }
220           }, result.getPrefixMatcher());
221           
222         }
223
224         for (Runnable runnable : chainedEtc) {
225           runnable.run();
226         }
227
228
229         final boolean searchInheritors = params.getInvocationCount() > 1;
230         if (searchInheritors) {
231           addExpectedTypeMembers(params, mergedInfos, false, noTypeCheck);
232         }
233       }
234     });
235
236     extend(CompletionType.SMART, or(
237       PsiJavaPatterns.psiElement().withParent(PsiNameValuePair.class),
238       PsiJavaPatterns.psiElement().withSuperParent(2, PsiNameValuePair.class)), new CompletionProvider<CompletionParameters>() {
239       public void addCompletions(@NotNull final CompletionParameters parameters, final ProcessingContext context, @NotNull final CompletionResultSet result) {
240         final PsiElement element = parameters.getPosition();
241         final ElementPattern<? extends PsiElement> leftNeighbor = PsiJavaPatterns.psiElement().afterLeaf(PsiJavaPatterns.psiElement().withText("."));
242         final boolean needQualify = leftNeighbor.accepts(element);
243
244         for (final PsiType type : ExpectedTypesGetter.getExpectedTypes(element, false)) {
245           final PsiClass psiClass = PsiUtil.resolveClassInType(type);
246           if (psiClass != null && psiClass.isAnnotationType()) {
247             final LookupItem item = JavaClassNameCompletionContributor.createClassLookupItem(psiClass, true);
248             if (needQualify) JavaCompletionUtil.qualify(item);
249             result.addElement(item);
250           }
251         }
252
253       }
254     });
255
256     extend(CompletionType.SMART, psiElement().inside(
257       psiElement(PsiDocTag.class).withName(
258         string().oneOf(PsiKeyword.THROWS, EXCEPTION_TAG))), new CompletionProvider<CompletionParameters>() {
259       public void addCompletions(@NotNull final CompletionParameters parameters, final ProcessingContext context, @NotNull final CompletionResultSet result) {
260         final PsiElement element = parameters.getPosition();
261         final Set<PsiClass> throwsSet = new HashSet<PsiClass>();
262         final PsiMethod method = PsiTreeUtil.getContextOfType(element, PsiMethod.class, true);
263         if(method != null){
264           for (PsiClassType ref : method.getThrowsList().getReferencedTypes()) {
265             final PsiClass exception = ref.resolve();
266             if (exception != null && throwsSet.add(exception)) {
267               result.addElement(TailTypeDecorator.withTail(new JavaPsiClassReferenceElement(exception), TailType.HUMBLE_SPACE_BEFORE_WORD));
268             }
269           }
270         }
271
272       }
273     });
274
275     final Key<PsiTryStatement> tryKey = Key.create("try");
276     extend(CompletionType.SMART, psiElement().afterLeaf(
277       psiElement().withText("("))
278       .withSuperParent(3, psiElement(PsiCatchSection.class).withParent(
279         psiElement(PsiTryStatement.class).save(tryKey))), new CompletionProvider<CompletionParameters>() {
280       protected void addCompletions(@NotNull final CompletionParameters parameters, final ProcessingContext context, @NotNull final CompletionResultSet result) {
281         final PsiCodeBlock tryBlock = context.get(tryKey).getTryBlock();
282         if (tryBlock == null) return;
283
284         for (final PsiClassType type : ExceptionUtil.getThrownExceptions(tryBlock.getStatements())) {
285           result.addElement(TailTypeDecorator.withTail(PsiTypeLookupItem.createLookupItem(type, tryBlock).setInsertHandler(new DefaultInsertHandler()), TailType.HUMBLE_SPACE_BEFORE_WORD));
286         }
287       }
288     });
289
290     extend(CompletionType.SMART, psiElement().inside(psiElement(PsiReferenceParameterList.class)),
291            new CompletionProvider<CompletionParameters>() {
292
293              protected void addCompletions(@NotNull final CompletionParameters parameters, final ProcessingContext processingContext, @NotNull final CompletionResultSet resultSet) {
294                final PsiElement context = parameters.getPosition();
295
296                final Pair<PsiClass, Integer> pair = getTypeParameterInfo(context);
297                if (pair == null) return;
298
299                final PsiClass referencedClass = pair.first;
300                final int parameterIndex = pair.second.intValue();
301                final PsiTypeParameter[] typeParameters = referencedClass.getTypeParameters();
302                final PsiTypeParameter targetParameter = typeParameters[parameterIndex];
303
304                boolean isLast = parameterIndex == typeParameters.length - 1;
305                final TailType tail = isLast ? new CharTailType('>') : TailType.COMMA;
306
307                PsiResolveHelper resolveHelper = JavaPsiFacade.getInstance(context.getProject()).getResolveHelper();
308                final PsiType[] psiTypes = ExpectedTypesGetter.getExpectedTypes(context, false);
309                if (psiTypes.length > 0) {
310                  for (PsiType type : psiTypes) {
311                    if (!(type instanceof PsiClassType)) continue;
312                    final PsiClassType.ClassResolveResult result = ((PsiClassType)type).resolveGenerics();
313                    final PsiClass typeClass = result.getElement();
314                    final PsiSubstitutor substitutor = result.getSubstitutor();
315
316                    if (!InheritanceUtil.isInheritorOrSelf(referencedClass, typeClass, true)) continue;
317
318                    final PsiSubstitutor currentSubstitutor =
319                      TypeConversionUtil.getClassSubstitutor(typeClass, referencedClass, PsiSubstitutor.EMPTY);
320                    for (PsiTypeParameter parameter : PsiUtil.typeParametersIterable(typeClass)) {
321                      final PsiType argSubstitution = substitutor.substitute(parameter);
322                      final PsiType paramSubstitution = currentSubstitutor.substitute(parameter);
323                      final PsiType substitution = resolveHelper
324                        .getSubstitutionForTypeParameter(targetParameter, paramSubstitution, argSubstitution, false,
325                                                         PsiUtil.getLanguageLevel(context));
326                      if (substitution != null && substitution != PsiType.NULL) {
327                        final LookupItem item = PsiTypeLookupItem.createLookupItem(substitution, context);
328                        resultSet.addElement(TailTypeDecorator.withTail(item.setInsertHandler(new DefaultInsertHandler()), tail));
329                      }
330                    }
331                  }
332                } else {
333                  final List<PsiClassType> typeList = Collections.singletonList((PsiClassType)TypeConversionUtil.typeParameterErasure(targetParameter));
334                  JavaInheritorsGetter
335                    .processInheritors(parameters, typeList, resultSet.getPrefixMatcher(), new Consumer<PsiType>() {
336                      public void consume(final PsiType type) {
337                        final PsiClass psiClass = PsiUtil.resolveClassInType(type);
338                        if (psiClass == null) return;
339
340                        resultSet.addElement(TailTypeDecorator.withTail(new JavaPsiClassReferenceElement(psiClass), tail));
341                      }
342                    });
343
344                }
345              }
346            });
347
348
349     extend(CompletionType.SMART, AFTER_NEW, new JavaInheritorsGetter(ConstructorInsertHandler.SMART_INSTANCE));
350   }
351
352   private static void addExpectedTypeMembers(CompletionParameters params,
353                                              THashSet<ExpectedTypeInfo> mergedInfos,
354                                              boolean quick,
355                                              Consumer<LookupElement> consumer) {
356     PsiElement position = params.getPosition();
357     if (!BasicExpressionCompletionContributor.AFTER_DOT.accepts(position)) {
358       for (ExpectedTypeInfo info : mergedInfos) {
359         new JavaMembersGetter(info.getType(), position).addMembers(params, !quick, consumer);
360         if (!info.getDefaultType().equals(info.getType())) {
361           new JavaMembersGetter(info.getDefaultType(), position).addMembers(params, !quick, consumer);
362         }
363       }
364     }
365   }
366
367   @Override
368   public void fillCompletionVariants(CompletionParameters parameters, CompletionResultSet result) {
369     super.fillCompletionVariants(parameters, JavaCompletionSorting.addJavaSorting(parameters, result));
370   }
371
372   public static SmartCompletionDecorator decorate(LookupElement lookupElement, Collection<ExpectedTypeInfo> infos) {
373     LookupItem item = lookupElement.as(LookupItem.CLASS_CONDITION_KEY);
374     if (item != null && item.getInsertHandler() == null) {
375       item.setInsertHandler(DefaultInsertHandler.NO_TAIL_HANDLER);
376     }
377
378     return new SmartCompletionDecorator(lookupElement, infos);
379   }
380
381   private static LookupElement createInstanceofLookupElement(PsiClass psiClass, Set<PsiClass> toWildcardInheritors) {
382     final PsiTypeParameter[] typeParameters = psiClass.getTypeParameters();
383     if (typeParameters.length > 0) {
384       for (final PsiClass parameterizedType : toWildcardInheritors) {
385         if (psiClass.isInheritor(parameterizedType, true)) {
386           PsiSubstitutor substitutor = PsiSubstitutor.EMPTY;
387           final PsiWildcardType wildcard = PsiWildcardType.createUnbounded(psiClass.getManager());
388           for (final PsiTypeParameter typeParameter : typeParameters) {
389             substitutor = substitutor.put(typeParameter, wildcard);
390           }
391           final PsiElementFactory factory = JavaPsiFacade.getElementFactory(psiClass.getProject());
392           return PsiTypeLookupItem.createLookupItem(factory.createType(psiClass, substitutor), psiClass);
393         }
394       }
395     }
396
397
398     return new JavaPsiClassReferenceElement(psiClass);
399   }
400
401   @Nullable
402   public static Pair<PsiClass, Integer> getTypeParameterInfo(PsiElement context) {
403     final PsiReferenceParameterList parameterList = PsiTreeUtil.getContextOfType(context, PsiReferenceParameterList.class, true);
404     if (parameterList == null) return null;
405
406     PsiElement parent = parameterList.getParent();
407     if (!(parent instanceof PsiJavaCodeReferenceElement)) return null;
408     
409     final PsiJavaCodeReferenceElement referenceElement = (PsiJavaCodeReferenceElement)parent;
410     final int parameterIndex;
411
412     int index = 0;
413     final PsiTypeElement typeElement = PsiTreeUtil.getContextOfType(context, PsiTypeElement.class, true);
414     if(typeElement != null){
415       final PsiTypeElement[] elements = referenceElement.getParameterList().getTypeParameterElements();
416       while (index < elements.length) {
417         final PsiTypeElement element = elements[index++];
418         if(element == typeElement) break;
419       }
420     }
421     parameterIndex = index - 1;
422
423     if(parameterIndex < 0) return null;
424     final PsiElement target = referenceElement.resolve();
425     if(!(target instanceof PsiClass)) return null;
426
427     final PsiClass referencedClass = (PsiClass)target;
428     final PsiTypeParameter[] typeParameters = referencedClass.getTypeParameters();
429     if(typeParameters.length <= parameterIndex) return null;
430
431     return Pair.create(referencedClass, parameterIndex);
432   }
433
434
435   @NotNull
436   public static ExpectedTypeInfo[] getExpectedTypes(final CompletionParameters parameters) {
437     final PsiElement position = parameters.getPosition();
438     if (psiElement().withParent(psiElement(PsiReferenceExpression.class).withParent(PsiThrowStatement.class)).accepts(position)) {
439       final PsiElementFactory factory = JavaPsiFacade.getInstance(position.getProject()).getElementFactory();
440       final PsiClassType classType = factory
441           .createTypeByFQClassName(CommonClassNames.JAVA_LANG_RUNTIME_EXCEPTION, position.getResolveScope());
442       final List<ExpectedTypeInfo> result = new SmartList<ExpectedTypeInfo>();
443       result.add(new ExpectedTypeInfoImpl(classType, ExpectedTypeInfo.TYPE_OR_SUBTYPE, 0, classType, TailType.SEMICOLON));
444       final PsiMethod method = PsiTreeUtil.getContextOfType(position, PsiMethod.class, true);
445       if (method != null) {
446         for (final PsiClassType type : method.getThrowsList().getReferencedTypes()) {
447           result.add(new ExpectedTypeInfoImpl(type, ExpectedTypeInfo.TYPE_OR_SUBTYPE, 0, type, TailType.SEMICOLON));
448         }
449       }
450       return result.toArray(new ExpectedTypeInfo[result.size()]);
451     }
452
453     PsiExpression expression = PsiTreeUtil.getContextOfType(position, PsiExpression.class, true);
454     if (expression == null) return ExpectedTypeInfo.EMPTY_ARRAY;
455
456     return ExpectedTypesProvider.getExpectedTypes(expression, true, parameters.getCompletionType() == CompletionType.SMART, false);
457   }
458
459   static Set<LookupElement> completeReference(final PsiElement element, PsiReference reference, final ElementFilter filter, final boolean acceptClasses, CompletionParameters parameters) {
460     if (reference instanceof PsiMultiReference) {
461       reference = ContainerUtil.findInstance(((PsiMultiReference) reference).getReferences(), PsiJavaReference.class);
462     }
463
464     if (reference instanceof PsiJavaReference) {
465       final PsiJavaReference javaReference = (PsiJavaReference)reference;
466
467       return JavaCompletionUtil.processJavaReference(element, javaReference, new ElementFilter() {
468         public boolean isAcceptable(Object element, PsiElement context) {
469           return filter.isAcceptable(element, context);
470         }
471
472         public boolean isClassAcceptable(Class hintClass) {
473           if (acceptClasses) {
474             return ReflectionCache.isAssignable(PsiClass.class, hintClass);
475           }
476
477           return ReflectionCache.isAssignable(PsiVariable.class, hintClass) ||
478                  ReflectionCache.isAssignable(PsiMethod.class, hintClass) ||
479                  ReflectionCache.isAssignable(CandidateInfo.class, hintClass);
480         }
481       }, true, parameters.getInvocationCount() <= 1, null, parameters);
482     }
483
484     return Collections.emptySet();
485   }
486
487   @Override
488   public void beforeCompletion(@NotNull CompletionInitializationContext context) {
489     if (context.getCompletionType() != CompletionType.SMART) {
490       return;
491     }
492
493     if (!context.getEditor().getSelectionModel().hasSelection()) {
494       final PsiFile file = context.getFile();
495       PsiElement element = file.findElementAt(context.getStartOffset());
496       if (element instanceof PsiIdentifier) {
497         element = element.getParent();
498         while (element instanceof PsiJavaCodeReferenceElement || element instanceof PsiCall ||
499                element instanceof PsiThisExpression || element instanceof PsiSuperExpression ||
500                element instanceof PsiTypeElement ||
501                element instanceof PsiClassObjectAccessExpression) {
502           int newEnd = element.getTextRange().getEndOffset();
503           if (element instanceof PsiMethodCallExpression) {
504             newEnd = ((PsiMethodCallExpression)element).getMethodExpression().getTextRange().getEndOffset();
505           }
506           else if (element instanceof PsiNewExpression) {
507             final PsiJavaCodeReferenceElement classReference = ((PsiNewExpression)element).getClassReference();
508             if (classReference != null) {
509               newEnd = classReference.getTextRange().getEndOffset();
510             }
511           }
512           context.setReplacementOffset(newEnd);
513           element = element.getParent();
514         }
515       }
516     }
517
518     PsiElement lastElement = context.getFile().findElementAt(context.getStartOffset() - 1);
519     if (lastElement != null && lastElement.getText().equals("(")) {
520       final PsiElement parent = lastElement.getParent();
521       if (parent instanceof PsiTypeCastExpression) {
522         context.setDummyIdentifier("");
523         return;
524       }
525       if (parent instanceof PsiParenthesizedExpression) {
526         context.setDummyIdentifier("xxx)yyy "); // to handle type cast
527         return;
528       }
529     }
530     context.setDummyIdentifier("xxx");
531   }
532 }