1 package com
.intellij
.codeInsight
.completion
;
3 import com
.intellij
.codeInsight
.CodeInsightSettings
;
4 import com
.intellij
.codeInsight
.ExpectedTypeInfo
;
5 import com
.intellij
.codeInsight
.ExpectedTypeInfoImpl
;
6 import com
.intellij
.codeInsight
.TailType
;
7 import com
.intellij
.codeInsight
.completion
.impl
.CamelHumpMatcher
;
8 import com
.intellij
.codeInsight
.completion
.scope
.CompletionElement
;
9 import com
.intellij
.codeInsight
.completion
.scope
.JavaCompletionProcessor
;
10 import com
.intellij
.codeInsight
.generation
.OverrideImplementUtil
;
11 import com
.intellij
.codeInsight
.lookup
.*;
12 import com
.intellij
.featureStatistics
.FeatureUsageTracker
;
13 import com
.intellij
.openapi
.diagnostic
.Logger
;
14 import com
.intellij
.openapi
.project
.Project
;
15 import com
.intellij
.openapi
.util
.*;
16 import com
.intellij
.openapi
.util
.text
.StringUtil
;
17 import static com
.intellij
.patterns
.PlatformPatterns
.psiElement
;
18 import com
.intellij
.patterns
.PsiElementPattern
;
19 import com
.intellij
.psi
.*;
20 import com
.intellij
.psi
.codeStyle
.*;
21 import com
.intellij
.psi
.filters
.ElementFilter
;
22 import com
.intellij
.psi
.html
.HtmlTag
;
23 import com
.intellij
.psi
.impl
.source
.PsiImmediateClassType
;
24 import com
.intellij
.psi
.infos
.CandidateInfo
;
25 import com
.intellij
.psi
.infos
.ClassCandidateInfo
;
26 import com
.intellij
.psi
.javadoc
.PsiDocToken
;
27 import com
.intellij
.psi
.scope
.BaseScopeProcessor
;
28 import com
.intellij
.psi
.scope
.ElementClassHint
;
29 import com
.intellij
.psi
.scope
.NameHint
;
30 import com
.intellij
.psi
.statistics
.JavaStatisticsManager
;
31 import com
.intellij
.psi
.tree
.IElementType
;
32 import com
.intellij
.psi
.util
.PropertyUtil
;
33 import com
.intellij
.psi
.util
.PsiTreeUtil
;
34 import com
.intellij
.psi
.util
.PsiUtil
;
35 import com
.intellij
.psi
.util
.TypeConversionUtil
;
36 import com
.intellij
.psi
.xml
.XmlToken
;
37 import com
.intellij
.psi
.xml
.XmlTokenType
;
38 import com
.intellij
.util
.ArrayUtil
;
39 import com
.intellij
.util
.NullableFunction
;
40 import com
.intellij
.util
.containers
.HashMap
;
41 import gnu
.trove
.THashSet
;
42 import org
.jetbrains
.annotations
.NonNls
;
43 import org
.jetbrains
.annotations
.NotNull
;
44 import org
.jetbrains
.annotations
.Nullable
;
48 public class JavaCompletionUtil
{
49 private static final Logger LOG
= Logger
.getInstance("#com.intellij.codeInsight.completion.JavaCompletionUtil");
51 static final Key
<SmartPsiElementPointer
> QUALIFIER_TYPE_ATTR
= Key
.create("qualifierType"); // SmartPsiElementPointer to PsiType of "qualifier"
53 public static final String GET_PREFIX
= "get";
55 public static final String SET_PREFIX
= "set";
57 public static final String IS_PREFIX
= "is";
58 private static final int MAX_SCOPE_SIZE_TO_SEARCH_UNRESOLVED
= 50000;
59 public static final OffsetKey LPAREN_OFFSET
= OffsetKey
.create("lparen");
60 public static final OffsetKey RPAREN_OFFSET
= OffsetKey
.create("rparen");
61 public static final OffsetKey ARG_LIST_END_OFFSET
= OffsetKey
.create("argListEnd");
62 static final NullableLazyKey
<ExpectedTypeInfo
[], CompletionLocation
> EXPECTED_TYPES
= NullableLazyKey
.create("expectedTypes", new NullableFunction
<CompletionLocation
, ExpectedTypeInfo
[]>() {
64 public ExpectedTypeInfo
[] fun(final CompletionLocation location
) {
65 return JavaSmartCompletionContributor
.getExpectedTypes(location
.getCompletionParameters());
68 private static final PsiElementPattern
.Capture
<PsiElement
> LEFT_PAREN
= psiElement(JavaTokenType
.LPARENTH
).andOr(psiElement().withParent(
69 PsiExpressionList
.class), psiElement().afterLeaf(".", PsiKeyword
.NEW
));
72 public static Set
<PsiType
> getExpectedTypes(final CompletionParameters parameters
) {
73 final PsiExpression expr
= PsiTreeUtil
.getContextOfType(parameters
.getPosition(), PsiExpression
.class, true);
75 final ExpectedTypeInfo
[] expectedInfos
= JavaSmartCompletionContributor
.getExpectedTypes(parameters
);
76 if(expectedInfos
!= null){
77 final Set
<PsiType
> set
= new THashSet
<PsiType
>();
78 for (final ExpectedTypeInfo expectedInfo
: expectedInfos
) {
79 set
.add(expectedInfo
.getType());
87 static final NullableLazyKey
<PsiMethod
, CompletionLocation
> POSITION_METHOD
= NullableLazyKey
.create("positionMethod", new NullableFunction
<CompletionLocation
, PsiMethod
>() {
88 public PsiMethod
fun(final CompletionLocation location
) {
89 return PsiTreeUtil
.getParentOfType(location
.getCompletionParameters().getPosition(), PsiMethod
.class, false);
92 public static final Key
<List
<PsiMethod
>> ALL_METHODS_ATTRIBUTE
= Key
.create("allMethods");
94 public static void completeLocalVariableName(Set
<LookupItem
> set
, PrefixMatcher matcher
, PsiVariable var
){
95 FeatureUsageTracker
.getInstance().triggerFeatureUsed("editing.completion.variable.name");
96 final JavaCodeStyleManager codeStyleManager
= JavaCodeStyleManager
.getInstance(var
.getProject());
97 final VariableKind variableKind
= codeStyleManager
.getVariableKind(var
);
99 String propertyName
= null;
100 if (variableKind
== VariableKind
.PARAMETER
) {
101 final PsiMethod method
= PsiTreeUtil
.getParentOfType(var
, PsiMethod
.class);
102 propertyName
= PropertyUtil
.getPropertyName(method
);
105 final PsiType type
= var
.getType();
106 SuggestedNameInfo suggestedNameInfo
= codeStyleManager
.suggestVariableName(variableKind
, propertyName
, null, type
);
107 final String
[] suggestedNames
= suggestedNameInfo
.names
;
108 tunePreferencePolicy(LookupItemUtil
.addLookupItems(set
, suggestedNames
, matcher
), suggestedNameInfo
);
110 if (type
.equalsToText(CommonClassNames
.JAVA_LANG_OBJECT
) && matcher
.prefixMatches("object")) {
111 set
.add(LookupElementFactoryImpl
.getInstance().createLookupElement("object"));
113 if (type
.equalsToText(CommonClassNames
.JAVA_LANG_STRING
) && matcher
.prefixMatches("string")) {
114 set
.add(LookupElementFactoryImpl
.getInstance().createLookupElement("string"));
119 suggestedNameInfo
= new SuggestedNameInfo(getOverlappedNameVersions(matcher
.getPrefix(), suggestedNames
, "")) {
120 public void nameChoosen(String name
) {
124 tunePreferencePolicy(LookupItemUtil
.addLookupItems(set
, suggestedNameInfo
.names
, matcher
), suggestedNameInfo
);
126 PsiElement parent
= PsiTreeUtil
.getParentOfType(var
, PsiCodeBlock
.class);
127 if(parent
== null) parent
= PsiTreeUtil
.getParentOfType(var
, PsiMethod
.class);
128 tunePreferencePolicy(LookupItemUtil
.addLookupItems(set
, getUnresolvedReferences(parent
, false), matcher
), suggestedNameInfo
);
129 final String
[] nameSuggestions
=
130 JavaStatisticsManager
.getNameSuggestions(type
, JavaStatisticsManager
.getContext(var
), matcher
.getPrefix());
131 tunePreferencePolicy(LookupItemUtil
.addLookupItems(set
, nameSuggestions
, matcher
), suggestedNameInfo
);
134 public static void completeFieldName(Set
<LookupItem
> set
, PsiVariable var
, final PrefixMatcher matcher
){
135 FeatureUsageTracker
.getInstance().triggerFeatureUsed("editing.completion.variable.name");
137 JavaCodeStyleManager codeStyleManager
= JavaCodeStyleManager
.getInstance(var
.getProject());
138 final VariableKind variableKind
= JavaCodeStyleManager
.getInstance(var
.getProject()).getVariableKind(var
);
140 final String prefix
= matcher
.getPrefix();
141 if (PsiType
.VOID
.equals(var
.getType()) || prefix
.startsWith(IS_PREFIX
) ||
142 prefix
.startsWith(GET_PREFIX
) ||
143 prefix
.startsWith(SET_PREFIX
)) {
144 completeVariableNameForRefactoring(var
.getProject(), set
, matcher
, var
.getType(), variableKind
);
148 SuggestedNameInfo suggestedNameInfo
= codeStyleManager
.suggestVariableName(variableKind
, null, null, var
.getType());
149 final String
[] suggestedNames
= suggestedNameInfo
.names
;
150 tunePreferencePolicy(LookupItemUtil
.addLookupItems(set
, suggestedNames
, matcher
), suggestedNameInfo
);
153 // use suggested names as suffixes
154 final String requiredSuffix
= codeStyleManager
.getSuffixByVariableKind(variableKind
);
155 if(variableKind
!= VariableKind
.STATIC_FINAL_FIELD
){
156 for (int i
= 0; i
< suggestedNames
.length
; i
++)
157 suggestedNames
[i
] = codeStyleManager
.variableNameToPropertyName(suggestedNames
[i
], variableKind
);
161 suggestedNameInfo
= new SuggestedNameInfo(getOverlappedNameVersions(prefix
, suggestedNames
, requiredSuffix
)) {
162 public void nameChoosen(String name
) {
166 tunePreferencePolicy(LookupItemUtil
.addLookupItems(set
, suggestedNameInfo
.names
, matcher
), suggestedNameInfo
);
169 tunePreferencePolicy(LookupItemUtil
.addLookupItems(set
, JavaStatisticsManager
.getNameSuggestions(var
.getType(), JavaStatisticsManager
.getContext(var
), matcher
.getPrefix()), matcher
), suggestedNameInfo
);
170 tunePreferencePolicy(LookupItemUtil
.addLookupItems(set
, getUnresolvedReferences(var
.getParent(), false),
171 matcher
), suggestedNameInfo
);
174 public static void completeMethodName(Set
<LookupItem
> set
, PsiElement element
, final PrefixMatcher matcher
){
175 if(element
instanceof PsiMethod
) {
176 final PsiMethod method
= (PsiMethod
)element
;
177 if (method
.isConstructor()) {
178 final PsiClass containingClass
= method
.getContainingClass();
179 final String name
= containingClass
.getName();
180 if (StringUtil
.isNotEmpty(name
)) {
181 LookupItemUtil
.addLookupItem(set
, name
, matcher
);
187 PsiClass ourClassParent
= PsiTreeUtil
.getParentOfType(element
, PsiClass
.class);
188 if (ourClassParent
== null) return;
189 LookupItemUtil
.addLookupItems(set
, getUnresolvedReferences(ourClassParent
, true), matcher
);
191 if(!((PsiModifierListOwner
)element
).hasModifierProperty(PsiModifier
.PRIVATE
)){
192 LookupItemUtil
.addLookupItems(set
, getOverides(ourClassParent
, PsiUtil
.getTypeByPsiElement(element
)),
194 LookupItemUtil
.addLookupItems(set
, getImplements(ourClassParent
, PsiUtil
.getTypeByPsiElement(element
)),
197 LookupItemUtil
.addLookupItems(set
, getPropertiesHandlersNames(
199 ((PsiModifierListOwner
)element
).hasModifierProperty(PsiModifier
.STATIC
),
200 PsiUtil
.getTypeByPsiElement(element
), element
), matcher
);
203 public static PsiType
getQualifierType(LookupItem item
) {
204 return (PsiType
)item
.getAttribute(QUALIFIER_TYPE_ATTR
);
207 public static void setQualifierType(LookupItem item
, PsiType type
) {
208 item
.setAttribute(QUALIFIER_TYPE_ATTR
, type
);
211 static void highlightMembersOfContainer(Set
<LookupItem
> set
) {
212 for (final LookupItem item
: set
) {
213 highlightMemberOfContainer(item
);
217 public static void highlightMemberOfContainer(@Nullable final LookupItem item
) {
221 Object o
= item
.getObject();
222 PsiType qualifierType
= getQualifierType(item
);
223 if (qualifierType
== null) return;
224 if (qualifierType
instanceof PsiArrayType
) {
225 if (o
instanceof PsiField
|| o
instanceof PsiMethod
|| o
instanceof PsiClass
) {
226 PsiElement parent
= ((PsiElement
)o
).getParent();
227 if (parent
instanceof PsiClass
&& parent
.getContainingFile().getVirtualFile() == null) { //?
228 item
.setAttribute(LookupItem
.HIGHLIGHTED_ATTR
, "");
232 else if (qualifierType
instanceof PsiClassType
) {
233 PsiClass qualifierClass
= ((PsiClassType
)qualifierType
).resolve();
234 if (o
instanceof PsiField
|| o
instanceof PsiMethod
|| o
instanceof PsiClass
) {
235 PsiElement parent
= ((PsiElement
)o
).getParent();
236 if (parent
!= null && parent
.equals(qualifierClass
)) {
237 item
.setAttribute(LookupItem
.HIGHLIGHTED_ATTR
, "");
243 public static void completeVariableNameForRefactoring(Project project
, Set
<LookupItem
> set
, String prefix
, PsiType varType
, VariableKind varKind
) {
244 completeVariableNameForRefactoring(project
, set
, new CamelHumpMatcher(prefix
), varType
, varKind
);
247 public static void completeVariableNameForRefactoring(Project project
, Set
<LookupItem
> set
, PrefixMatcher matcher
, PsiType varType
, VariableKind varKind
) {
248 JavaCodeStyleManager codeStyleManager
= JavaCodeStyleManager
.getInstance(project
);
249 SuggestedNameInfo suggestedNameInfo
= codeStyleManager
.suggestVariableName(varKind
, null, null, varType
);
250 final String
[] strings
= completeVariableNameForRefactoring(codeStyleManager
, matcher
, varType
, varKind
, suggestedNameInfo
);
251 tunePreferencePolicy(LookupItemUtil
.addLookupItems(set
, strings
, matcher
), suggestedNameInfo
);
254 public static String
[] completeVariableNameForRefactoring(JavaCodeStyleManager codeStyleManager
, final PsiType varType
,
255 final VariableKind varKind
,
256 SuggestedNameInfo suggestedNameInfo
) {
257 return completeVariableNameForRefactoring(codeStyleManager
, new CamelHumpMatcher(""), varType
, varKind
, suggestedNameInfo
);
260 public static String
[] completeVariableNameForRefactoring(JavaCodeStyleManager codeStyleManager
, final PrefixMatcher matcher
, final PsiType varType
,
261 final VariableKind varKind
,
262 SuggestedNameInfo suggestedNameInfo
) {
263 Set
<String
> result
= new LinkedHashSet
<String
>();
264 final String
[] suggestedNames
= suggestedNameInfo
.names
;
265 for (final String suggestedName
: suggestedNames
) {
266 if (matcher
.prefixMatches(suggestedName
)) {
267 result
.add(suggestedName
);
271 if (result
.isEmpty() && PsiType
.VOID
!= varType
) {
272 // use suggested names as suffixes
273 final String requiredSuffix
= codeStyleManager
.getSuffixByVariableKind(varKind
);
274 final String prefix
= matcher
.getPrefix();
275 final boolean isMethodPrefix
= prefix
.startsWith(IS_PREFIX
) || prefix
.startsWith(GET_PREFIX
) || prefix
.startsWith(SET_PREFIX
);
276 if (varKind
!= VariableKind
.STATIC_FINAL_FIELD
|| isMethodPrefix
) {
277 for (int i
= 0; i
< suggestedNames
.length
; i
++) {
278 suggestedNames
[i
] = codeStyleManager
.variableNameToPropertyName(suggestedNames
[i
], varKind
);
282 result
.addAll(Arrays
.asList(getOverlappedNameVersions(prefix
, suggestedNames
, requiredSuffix
)));
286 return result
.toArray(new String
[result
.size()]);
289 private static void tunePreferencePolicy(final List
<LookupItem
> list
, final SuggestedNameInfo suggestedNameInfo
) {
290 final InsertHandler insertHandler
= new InsertHandler() {
291 public void handleInsert(final InsertionContext context
, final LookupElement item
) {
292 suggestedNameInfo
.nameChoosen(item
.getLookupString());
296 for (int i
= 0; i
< list
.size(); i
++) {
297 LookupItem item
= list
.get(i
);
298 item
.setPriority(list
.size() - i
).setInsertHandler(insertHandler
);
302 public static String
[] getOverlappedNameVersions(final String prefix
, final String
[] suggestedNames
, String suffix
) {
303 final List
<String
> newSuggestions
= new ArrayList
<String
>();
304 int longestOverlap
= 0;
306 for (String suggestedName
: suggestedNames
) {
307 if (suggestedName
.toUpperCase().startsWith(prefix
.toUpperCase())) {
308 newSuggestions
.add(suggestedName
);
309 longestOverlap
= prefix
.length();
312 suggestedName
= String
.valueOf(Character
.toUpperCase(suggestedName
.charAt(0))) + suggestedName
.substring(1);
313 final int overlap
= getOverlap(suggestedName
, prefix
);
315 if (overlap
< longestOverlap
) continue;
317 if (overlap
> longestOverlap
) {
318 newSuggestions
.clear();
319 longestOverlap
= overlap
;
322 String suggestion
= prefix
.substring(0, prefix
.length() - overlap
) + suggestedName
;
324 final int lastIndexOfSuffix
= suggestion
.lastIndexOf(suffix
);
325 if (lastIndexOfSuffix
>= 0 && suffix
.length() < suggestion
.length() - lastIndexOfSuffix
) {
326 suggestion
= suggestion
.substring(0, lastIndexOfSuffix
) + suffix
;
329 if (!newSuggestions
.contains(suggestion
)) {
330 newSuggestions
.add(suggestion
);
333 return newSuggestions
.toArray(new String
[newSuggestions
.size()]);
336 static int getOverlap(final String propertyName
, final String prefix
) {
338 int propertyNameLen
= propertyName
.length();
339 int prefixLen
= prefix
.length();
340 for (int j
= 1; j
< prefixLen
&& j
< propertyNameLen
; j
++) {
341 if (prefix
.substring(prefixLen
- j
).equals(propertyName
.substring(0, j
))) {
348 public static PsiType
eliminateWildcards(PsiType type
) {
349 return eliminateWildcardsInner(type
, true);
352 static PsiType
eliminateWildcardsInner(PsiType type
, final boolean eliminateInTypeArguments
) {
353 if (eliminateInTypeArguments
&& type
instanceof PsiClassType
) {
354 PsiClassType classType
= ((PsiClassType
)type
);
355 JavaResolveResult resolveResult
= classType
.resolveGenerics();
356 PsiClass aClass
= (PsiClass
)resolveResult
.getElement();
357 if (aClass
!= null) {
358 PsiManager manager
= aClass
.getManager();
359 PsiTypeParameter
[] typeParams
= aClass
.getTypeParameters();
360 Map
<PsiTypeParameter
, PsiType
> map
= new HashMap
<PsiTypeParameter
, PsiType
>();
361 for (PsiTypeParameter typeParam
: typeParams
) {
362 PsiType substituted
= resolveResult
.getSubstitutor().substitute(typeParam
);
363 if (substituted
instanceof PsiWildcardType
) {
364 substituted
= ((PsiWildcardType
)substituted
).getBound();
365 if (substituted
== null) substituted
= PsiType
.getJavaLangObject(manager
, aClass
.getResolveScope());
367 map
.put(typeParam
, substituted
);
370 PsiElementFactory factory
= JavaPsiFacade
.getInstance(manager
.getProject()).getElementFactory();
371 PsiSubstitutor substitutor
= factory
.createSubstitutor(map
);
372 type
= factory
.createType(aClass
, substitutor
);
375 else if (type
instanceof PsiArrayType
) {
376 return eliminateWildcardsInner(((PsiArrayType
)type
).getComponentType(), false).createArrayType();
378 else if (type
instanceof PsiWildcardType
) {
379 return ((PsiWildcardType
)type
).getExtendsBound();
384 public static String
[] getOverides(final PsiClass parent
, final PsiType typeByPsiElement
) {
385 final List
<String
> overides
= new ArrayList
<String
>();
386 final Collection
<CandidateInfo
> methodsToOverrideImplement
= OverrideImplementUtil
.getMethodsToOverrideImplement(parent
, true);
387 for (final CandidateInfo candidateInfo
: methodsToOverrideImplement
) {
388 final PsiElement element
= candidateInfo
.getElement();
389 if (Comparing
.equal(typeByPsiElement
, PsiUtil
.getTypeByPsiElement(element
)) && element
instanceof PsiNamedElement
) {
390 overides
.add(((PsiNamedElement
)element
).getName());
393 return ArrayUtil
.toStringArray(overides
);
396 public static String
[] getImplements(final PsiClass parent
, final PsiType typeByPsiElement
) {
397 final List
<String
> overides
= new ArrayList
<String
>();
398 final Collection
<CandidateInfo
> methodsToOverrideImplement
= OverrideImplementUtil
.getMethodsToOverrideImplement(parent
, false);
399 for (final CandidateInfo candidateInfo
: methodsToOverrideImplement
) {
400 final PsiElement element
= candidateInfo
.getElement();
401 if (Comparing
.equal(typeByPsiElement
,PsiUtil
.getTypeByPsiElement(element
)) && element
instanceof PsiNamedElement
) {
402 overides
.add(((PsiNamedElement
)element
).getName());
405 return ArrayUtil
.toStringArray(overides
);
408 public static String
[] getPropertiesHandlersNames(final PsiClass psiClass
,
409 final boolean staticContext
,
410 final PsiType varType
,
411 final PsiElement element
) {
412 class Change
implements Runnable
{
413 private String
[] result
;
416 final List
<String
> propertyHandlers
= new ArrayList
<String
>();
417 final PsiField
[] fields
= psiClass
.getFields();
419 for (final PsiField field
: fields
) {
420 if (field
== element
) continue;
421 final PsiModifierList modifierList
= field
.getModifierList();
422 if (staticContext
&& (modifierList
!= null && !modifierList
.hasModifierProperty(PsiModifier
.STATIC
))) continue;
424 if (field
.getType().equals(varType
)) {
425 final String getterName
= PropertyUtil
.suggestGetterName(field
.getProject(), field
);
426 if ((psiClass
.findMethodsByName(getterName
, true).length
== 0 ||
427 psiClass
.findMethodBySignature(PropertyUtil
.generateGetterPrototype(field
), true) == null)) {
428 propertyHandlers
.add(getterName
);
432 if (PsiType
.VOID
.equals(varType
)) {
433 final String setterName
= PropertyUtil
.suggestSetterName(field
.getProject(), field
);
434 if ((psiClass
.findMethodsByName(setterName
, true).length
== 0 ||
435 psiClass
.findMethodBySignature(PropertyUtil
.generateSetterPrototype(field
), true) == null)) {
436 propertyHandlers
.add(setterName
);
440 result
= ArrayUtil
.toStringArray(propertyHandlers
);
443 final Change result
= new Change();
444 element
.getManager().performActionWithFormatterDisabled(result
);
445 return result
.result
;
448 public static boolean isInExcludedPackage(@NotNull final PsiClass psiClass
) {
449 final String name
= psiClass
.getQualifiedName();
450 if (name
== null) return false;
451 CodeInsightSettings cis
= CodeInsightSettings
.getInstance();
452 boolean isExcluded
= false;
453 for (String packages
: cis
.EXCLUDED_PACKAGES
) {
454 if (name
.startsWith(packages
)) {
462 public static PsiField
getOriginalElement(@NotNull PsiField field
) {
463 final PsiClass cls
= field
.getContainingClass();
465 final PsiClass newParent
= getOriginalElement(cls
);
466 if (newParent
!= cls
) {
467 final PsiField original
= newParent
.findFieldByName(field
.getName(), false);
468 if (original
!= null) {
477 public static PsiTypeParameter
getOriginalElement(@NotNull PsiTypeParameter param
) {
478 final PsiClass parent
= PsiTreeUtil
.getParentOfType(param
, PsiClass
.class, true, PsiMethod
.class);
479 if (parent
!= null) {
480 final PsiClass newParent
= getOriginalElement(parent
);
481 if (newParent
!= parent
) {
482 for (PsiTypeParameter parameter
: newParent
.getTypeParameters()) {
483 if (parameter
.getName().equals(param
.getName())) {
493 public static PsiClass
getOriginalElement(@NotNull PsiClass cls
) {
494 final PsiClass containingClass
= cls
.getContainingClass();
495 if (containingClass
!= null) {
496 final PsiClass newParent
= getOriginalElement(containingClass
);
497 if (newParent
!= containingClass
) {
498 return findClassByName(cls
, newParent
.getInnerClasses());
502 final PsiFile containingFile
= cls
.getContainingFile();
503 if (containingFile
instanceof PsiClassOwner
) {
504 return findClassByName(cls
, ((PsiClassOwner
)containingFile
.getOriginalFile()).getClasses());
510 private static PsiClass
findClassByName(PsiClass defResult
, PsiClass
[] classes
) {
511 String name
= defResult
.getName();
512 if (name
== null) return defResult
;
514 for (PsiClass candidate
: classes
) {
515 if (name
.equals(candidate
.getName())) {
522 @SuppressWarnings({"unchecked"})
524 public static <T
extends PsiType
> T
originalize(@NotNull T type
) {
525 if (!type
.isValid()) {
529 return (T
)type
.accept(new PsiTypeVisitor
<PsiType
>() {
531 public PsiType
visitArrayType(final PsiArrayType arrayType
) {
532 return new PsiArrayType(originalize(arrayType
.getComponentType()));
535 public PsiType
visitCapturedWildcardType(final PsiCapturedWildcardType capturedWildcardType
) {
536 return PsiCapturedWildcardType
.create(originalize(capturedWildcardType
.getWildcard()), capturedWildcardType
.getContext());
539 public PsiType
visitClassType(final PsiClassType classType
) {
540 final PsiClassType
.ClassResolveResult classResolveResult
= classType
.resolveGenerics();
541 final PsiClass psiClass
= classResolveResult
.getElement();
542 final PsiSubstitutor substitutor
= classResolveResult
.getSubstitutor();
543 if (psiClass
== null) return classType
;
545 LOG
.assertTrue(psiClass
.isValid());
547 return new PsiImmediateClassType(getOriginalElement(psiClass
), originalize(substitutor
));
550 public PsiType
visitEllipsisType(final PsiEllipsisType ellipsisType
) {
551 return new PsiEllipsisType(originalize(ellipsisType
.getComponentType()));
554 public PsiType
visitPrimitiveType(final PsiPrimitiveType primitiveType
) {
555 return primitiveType
;
558 public PsiType
visitType(final PsiType type
) {
562 public PsiType
visitWildcardType(final PsiWildcardType wildcardType
) {
563 final PsiType bound
= wildcardType
.getBound();
564 final PsiManager manager
= wildcardType
.getManager();
565 if (bound
== null) return PsiWildcardType
.createUnbounded(manager
);
566 return wildcardType
.isExtends() ? PsiWildcardType
.createExtends(manager
, bound
) : PsiWildcardType
.createSuper(manager
, bound
);
572 private static PsiSubstitutor
originalize(@Nullable final PsiSubstitutor substitutor
) {
573 if (substitutor
== null) return null;
575 PsiSubstitutor originalSubstitutor
= PsiSubstitutor
.EMPTY
;
576 for (final Map
.Entry
<PsiTypeParameter
, PsiType
> entry
: substitutor
.getSubstitutionMap().entrySet()) {
577 final PsiType value
= entry
.getValue();
578 originalSubstitutor
= originalSubstitutor
.put(getOriginalElement(entry
.getKey()), value
== null ?
null : originalize(value
));
580 return originalSubstitutor
;
583 public static String
[] getUnresolvedReferences(final PsiElement parentOfType
, final boolean referenceOnMethod
) {
584 if (parentOfType
!= null && parentOfType
.getTextLength() > MAX_SCOPE_SIZE_TO_SEARCH_UNRESOLVED
) return ArrayUtil
.EMPTY_STRING_ARRAY
;
585 final List
<String
> unresolvedRefs
= new ArrayList
<String
>();
587 if (parentOfType
!= null) {
588 parentOfType
.accept(new JavaRecursiveElementWalkingVisitor() {
589 @Override public void visitReferenceExpression(PsiReferenceExpression reference
) {
590 final PsiElement parent
= reference
.getParent();
591 if (parent
instanceof PsiReference
) return;
592 if (referenceOnMethod
&& parent
instanceof PsiMethodCallExpression
&&
593 reference
== ((PsiMethodCallExpression
)parent
).getMethodExpression()) {
594 if (reference
.resolve() == null && reference
.getReferenceName() != null) unresolvedRefs
.add(reference
.getReferenceName());
596 else if (!referenceOnMethod
&& !(parent
instanceof PsiMethodCallExpression
) &&reference
.resolve() == null && reference
.getReferenceName() != null) {
597 unresolvedRefs
.add(reference
.getReferenceName());
602 return ArrayUtil
.toStringArray(unresolvedRefs
);
605 public static void initOffsets(final PsiFile file
, final Project project
, final OffsetMap offsetMap
, CompletionType type
){
606 int selectionEndOffset
= offsetMap
.getOffset(CompletionInitializationContext
.SELECTION_END_OFFSET
);
608 PsiElement element
= file
.findElementAt(selectionEndOffset
);
609 if (element
== null) return;
611 if (LEFT_PAREN
.accepts(element
)) {
612 selectionEndOffset
--;
613 element
= file
.findElementAt(selectionEndOffset
);
614 if (element
== null) return;
617 final PsiReference reference
= file
.findReferenceAt(selectionEndOffset
);
618 if(reference
!= null) {
619 if(reference
instanceof PsiJavaCodeReferenceElement
){
620 offsetMap
.addOffset(CompletionInitializationContext
.IDENTIFIER_END_OFFSET
, element
.getParent().getTextRange().getEndOffset());
623 offsetMap
.addOffset(CompletionInitializationContext
.IDENTIFIER_END_OFFSET
,
624 reference
.getElement().getTextRange().getStartOffset() + reference
.getRangeInElement().getEndOffset());
627 element
= file
.findElementAt(offsetMap
.getOffset(CompletionInitializationContext
.IDENTIFIER_END_OFFSET
));
629 else if (isWord(element
)){
630 if(element
instanceof PsiIdentifier
&& element
.getParent() instanceof PsiJavaCodeReferenceElement
){
631 offsetMap
.addOffset(CompletionInitializationContext
.IDENTIFIER_END_OFFSET
, element
.getParent().getTextRange().getEndOffset());
634 offsetMap
.addOffset(CompletionInitializationContext
.IDENTIFIER_END_OFFSET
, element
.getTextRange().getEndOffset());
637 element
= file
.findElementAt(offsetMap
.getOffset(CompletionInitializationContext
.IDENTIFIER_END_OFFSET
));
638 if (element
== null) return;
641 if (element
instanceof PsiWhiteSpace
&&
642 ( !element
.textContains('\n') ||
643 CodeStyleSettingsManager
.getInstance(project
).getCurrentSettings().METHOD_PARAMETERS_LPAREN_ON_NEXT_LINE
646 element
= file
.findElementAt(element
.getTextRange().getEndOffset());
649 if (LEFT_PAREN
.accepts(element
)) {
650 offsetMap
.addOffset(LPAREN_OFFSET
, element
.getTextRange().getStartOffset());
651 PsiElement list
= element
.getParent();
652 PsiElement last
= list
.getLastChild();
653 if (last
instanceof PsiJavaToken
&& ((PsiJavaToken
)last
).getTokenType() == JavaTokenType
.RPARENTH
) {
654 offsetMap
.addOffset(RPAREN_OFFSET
, last
.getTextRange().getStartOffset());
658 offsetMap
.addOffset(ARG_LIST_END_OFFSET
, list
.getTextRange().getEndOffset());
663 static boolean isWord(PsiElement element
) {
664 if (element
instanceof PsiIdentifier
){
667 else if (element
instanceof PsiKeyword
){
670 else if (element
instanceof PsiJavaToken
){
671 final String text
= element
.getText();
672 if(PsiKeyword
.TRUE
.equals(text
)) return true;
673 if(PsiKeyword
.FALSE
.equals(text
)) return true;
674 if(PsiKeyword
.NULL
.equals(text
)) return true;
677 else if (element
instanceof PsiDocToken
) {
678 IElementType tokenType
= ((PsiDocToken
)element
).getTokenType();
679 return tokenType
== JavaDocTokenType
.DOC_TAG_VALUE_TOKEN
|| tokenType
== JavaDocTokenType
.DOC_TAG_NAME
;
681 else if (element
instanceof XmlToken
) {
682 IElementType tokenType
= ((XmlToken
)element
).getTokenType();
683 return tokenType
== XmlTokenType
.XML_TAG_NAME
||
684 tokenType
== XmlTokenType
.XML_NAME
||
685 tokenType
== XmlTokenType
.XML_ATTRIBUTE_VALUE_TOKEN
||
686 // html data chars contains whitespaces
687 (tokenType
== XmlTokenType
.XML_DATA_CHARACTERS
&& !(element
.getParent() instanceof HtmlTag
));
694 public static void resetParensInfo(final OffsetMap offsetMap
) {
695 offsetMap
.removeOffset(LPAREN_OFFSET
);
696 offsetMap
.removeOffset(RPAREN_OFFSET
);
697 offsetMap
.removeOffset(ARG_LIST_END_OFFSET
);
698 offsetMap
.removeOffset(CompletionInitializationContext
.IDENTIFIER_END_OFFSET
);
702 public static List
<?
extends PsiElement
> getAllPsiElements(final LookupElement item
) {
703 List
<PsiMethod
> allMethods
= item
.getUserData(ALL_METHODS_ATTRIBUTE
);
704 if (allMethods
!= null) return allMethods
;
705 if (item
.getObject() instanceof PsiElement
) return Arrays
.asList((PsiElement
)item
.getObject());
710 public static PsiType
getPsiType(final Object o
) {
711 if (o
instanceof PsiVariable
) {
712 return ((PsiVariable
)o
).getType();
714 else if (o
instanceof PsiMethod
) {
715 return ((PsiMethod
)o
).getReturnType();
717 else if (o
instanceof PsiClass
) {
718 final PsiClass psiClass
= (PsiClass
)o
;
719 return JavaPsiFacade
.getInstance(psiClass
.getProject()).getElementFactory().createType(psiClass
);
721 else if (o
instanceof PsiExpression
) {
722 return ((PsiExpression
)o
).getType();
727 public static int getNameEndMatchingDegree(final String name
, ExpectedTypeInfo
[] expectedInfos
, String prefix
) {
729 if (name
!= null && expectedInfos
!= null) {
730 if (prefix
.equals(name
)) {
731 res
= Integer
.MAX_VALUE
;
733 final List
<String
> words
= NameUtil
.nameToWordsLowerCase(name
);
734 final List
<String
> wordsNoDigits
= NameUtil
.nameToWordsLowerCase(truncDigits(name
));
735 int max1
= calcMatch(words
, 0, expectedInfos
);
736 max1
= calcMatch(wordsNoDigits
, max1
, expectedInfos
);
744 static String
truncDigits(String name
){
745 int count
= name
.length() - 1;
747 char c
= name
.charAt(count
);
748 if (!Character
.isDigit(c
)) break;
751 return name
.substring(0, count
+ 1);
754 static int calcMatch(final List
<String
> words
, int max
, ExpectedTypeInfo
[] myExpectedInfos
) {
755 for (ExpectedTypeInfo myExpectedInfo
: myExpectedInfos
) {
756 String expectedName
= ((ExpectedTypeInfoImpl
)myExpectedInfo
).expectedName
;
757 if (expectedName
== null) continue;
758 max
= calcMatch(expectedName
, words
, max
);
759 max
= calcMatch(truncDigits(expectedName
), words
, max
);
764 static int calcMatch(final String expectedName
, final List
<String
> words
, int max
) {
765 if (expectedName
== null) return max
;
767 String
[] expectedWords
= NameUtil
.nameToWords(expectedName
);
768 int limit
= Math
.min(words
.size(), expectedWords
.length
);
769 for (int i
= 0; i
< limit
; i
++) {
770 String word
= words
.get(words
.size() - i
- 1);
771 String expectedWord
= expectedWords
[expectedWords
.length
- i
- 1];
772 if (word
.equalsIgnoreCase(expectedWord
)) {
773 max
= Math
.max(max
, i
+ 1);
783 static String
getLookupObjectName(Object o
) {
784 if (o
instanceof PsiVariable
) {
785 final PsiVariable variable
= (PsiVariable
)o
;
786 JavaCodeStyleManager codeStyleManager
= JavaCodeStyleManager
.getInstance(variable
.getProject());
787 VariableKind variableKind
= codeStyleManager
.getVariableKind(variable
);
788 return codeStyleManager
.variableNameToPropertyName(variable
.getName(), variableKind
);
790 if (o
instanceof PsiMethod
) {
791 return ((PsiMethod
)o
).getName();
797 public static PsiType
getLookupElementType(final LookupElement element
) {
798 PsiType qualifierType
= null;
799 if (element
instanceof TypedLookupItem
) {
800 return ((TypedLookupItem
)element
).getType();
803 final Object o
= element
.getObject();
804 if (o
instanceof PsiVariable
) {
805 qualifierType
= ((PsiVariable
)o
).getType();
807 else if (o
instanceof PsiMethod
) {
808 qualifierType
= ((PsiMethod
)o
).getReturnType();
810 else if (o
instanceof PsiExpression
) {
811 qualifierType
= ((PsiExpression
) o
).getType();
814 final LookupItem lookupItem
= element
.as(LookupItem
.class);
815 if (lookupItem
!= null) {
816 final PsiSubstitutor substitutor
= (PsiSubstitutor
)lookupItem
.getAttribute(LookupItem
.SUBSTITUTOR
);
817 if (substitutor
!= null) {
818 qualifierType
= substitutor
.substitute(qualifierType
);
821 return qualifierType
;
825 public static PsiType
getQualifiedMemberReferenceType(@Nullable PsiType qualifierType
, @NotNull final PsiMember member
) {
826 final ClassCandidateInfo info
= TypeConversionUtil
.splitType(qualifierType
, member
);
831 final PsiClass element
= info
.getElement();
832 assert element
!= null;
834 final Ref
<PsiSubstitutor
> subst
= Ref
.create(PsiSubstitutor
.EMPTY
);
835 class MyProcessor
extends BaseScopeProcessor
implements NameHint
, ElementClassHint
{
836 public boolean execute(PsiElement element
, ResolveState state
) {
837 if (element
== member
) {
838 subst
.set(state
.get(PsiSubstitutor
.KEY
));
843 public String
getName(ResolveState state
) {
844 return member
.getName();
847 public boolean shouldProcess(DeclaractionKind kind
) {
848 return member
instanceof PsiEnumConstant ? kind
== DeclaractionKind
.ENUM_CONST
:
849 member
instanceof PsiField ? kind
== DeclaractionKind
.FIELD
:
850 kind
== DeclaractionKind
.METHOD
;
854 public <T
> T
getHint(Key
<T
> hintKey
) {
855 return hintKey
== NameHint
.KEY
|| hintKey
== ElementClassHint
.KEY ?
(T
)this : null;
859 element
.processDeclarations(new MyProcessor(), ResolveState
.initial().put(PsiSubstitutor
.KEY
, info
.getSubstitutor()), null, member
);
861 PsiType rawType
= member
instanceof PsiField ?
((PsiField
) member
).getType() : ((PsiMethod
) member
).getReturnType();
862 return subst
.get().substitute(rawType
);
865 public static Set
<LookupElement
> processJavaReference(PsiElement element
, PsiJavaReference javaReference
, ElementFilter elementFilter
,
866 final boolean checkAccess
, @Nullable final PrefixMatcher matcher
) {
867 final THashSet
<LookupElement
> set
= new THashSet
<LookupElement
>();
868 final Condition
<String
> nameCondition
= matcher
== null ?
null : new Condition
<String
>() {
869 public boolean value(String s
) {
870 return matcher
.prefixMatches(s
);
873 final JavaCompletionProcessor processor
= new JavaCompletionProcessor(element
, elementFilter
, checkAccess
, nameCondition
);
874 javaReference
.processVariants(processor
);
876 for (CompletionElement completionElement
: processor
.getResults()) {
877 addLookupItem(set
, completionElement
.getElement(), completionElement
.getSubstitutor(), completionElement
.getQualifier());
882 static void addLookupItem(Set
<LookupElement
> set
, @NotNull Object completion
, @Nullable PsiSubstitutor substitutor
,
883 @Nullable PsiType qualifier
) {
884 assert !(completion
instanceof LookupElement
);
886 LookupItem
<?
> ret
= LookupItemUtil
.objectToLookupItem(completion
);
887 if(ret
== null) return;
889 if (substitutor
!= null) {
890 ret
.setAttribute(LookupItem
.SUBSTITUTOR
, substitutor
);
893 if (qualifier
!= null) {
894 ret
.setAttribute(QUALIFIER_TYPE_ATTR
, qualifier
);
900 public static final Key
<TailType
> COMPLETION_TAIL_TYPE
= Key
.create("COMPLETION_TAIL_TYPE");
903 * Use this method to set avoid unwanted tail symbols while using completion inside
904 * light-editors like EditorComboBox or EditorTextField.
906 * @author Konstantin Bulenkov
908 * @param file document holder
909 * @param type new TailType
911 * @see com.intellij.codeInsight.TailType
912 * @see com.intellij.codeInsight.completion.JavaCompletionContributor#setTailTypeByFile(com.intellij.codeInsight.lookup.LookupElement, com.intellij.psi.PsiFile)
914 public static void setDefaultTailTypeForFile(PsiFile file
, TailType type
) {
915 if (file
!= null && type
!= null) {
916 file
.putUserData(COMPLETION_TAIL_TYPE
, type
);
921 public static TailType
getDefaultTailTypeForFile(PsiFile file
) {
922 return file
== null ?
null : file
.getUserData(COMPLETION_TAIL_TYPE
);