remove ending dots in package completion
[fedora-idea.git] / codeInsight / impl / com / intellij / codeInsight / completion / JavaCompletionUtil.java
blobd9e0f8b16a6772e1724253aec6f849d6e6587070
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;
46 import java.util.*;
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"
52 @NonNls
53 public static final String GET_PREFIX = "get";
54 @NonNls
55 public static final String SET_PREFIX = "set";
56 @NonNls
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[]>() {
63 @Nullable
64 public ExpectedTypeInfo[] fun(final CompletionLocation location) {
65 return JavaSmartCompletionContributor.getExpectedTypes(location.getCompletionParameters());
67 });
68 private static final PsiElementPattern.Capture<PsiElement> LEFT_PAREN = psiElement(JavaTokenType.LPARENTH).andOr(psiElement().withParent(
69 PsiExpressionList.class), psiElement().afterLeaf(".", PsiKeyword.NEW));
71 @Nullable
72 public static Set<PsiType> getExpectedTypes(final CompletionParameters parameters) {
73 final PsiExpression expr = PsiTreeUtil.getContextOfType(parameters.getPosition(), PsiExpression.class, true);
74 if (expr != null) {
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());
81 return set;
84 return null;
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);
91 });
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);
109 if (set.isEmpty()) {
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"));
118 if (set.isEmpty()) {
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);
145 return;
148 SuggestedNameInfo suggestedNameInfo = codeStyleManager.suggestVariableName(variableKind, null, null, var.getType());
149 final String[] suggestedNames = suggestedNameInfo.names;
150 tunePreferencePolicy(LookupItemUtil.addLookupItems(set, suggestedNames, matcher), suggestedNameInfo);
152 if (set.isEmpty()) {
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);
183 return;
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)),
193 matcher);
194 LookupItemUtil.addLookupItems(set, getImplements(ourClassParent, PsiUtil.getTypeByPsiElement(element)),
195 matcher);
197 LookupItemUtil.addLookupItems(set, getPropertiesHandlersNames(
198 ourClassParent,
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) {
218 if (item == null) {
219 return;
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) {
337 int overlap = 0;
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))) {
342 overlap = j;
345 return overlap;
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();
381 return type;
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;
415 public void run() {
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)) {
455 isExcluded = true;
458 return isExcluded;
461 @NotNull
462 public static PsiField getOriginalElement(@NotNull PsiField field) {
463 final PsiClass cls = field.getContainingClass();
464 if (cls != null) {
465 final PsiClass newParent = getOriginalElement(cls);
466 if (newParent != cls) {
467 final PsiField original = newParent.findFieldByName(field.getName(), false);
468 if (original != null) {
469 return original;
473 return field;
476 @NotNull
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())) {
484 return parameter;
489 return param;
492 @NotNull
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());
507 return cls;
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())) {
516 return candidate;
519 return defResult;
522 @SuppressWarnings({"unchecked"})
523 @NotNull
524 public static <T extends PsiType> T originalize(@NotNull T type) {
525 if (!type.isValid()) {
526 return type;
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) {
559 return 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);
571 @Nullable
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());
622 else{
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());
633 else{
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){
665 return true;
667 else if (element instanceof PsiKeyword){
668 return true;
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;
675 return false;
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));
689 else{
690 return false;
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);
701 @Nullable
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());
706 return null;
709 @Nullable
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();
724 return null;
727 public static int getNameEndMatchingDegree(final String name, ExpectedTypeInfo[] expectedInfos, String prefix) {
728 int res = 0;
729 if (name != null && expectedInfos != null) {
730 if (prefix.equals(name)) {
731 res = Integer.MAX_VALUE;
732 } else {
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);
737 res = max1;
741 return res;
744 static String truncDigits(String name){
745 int count = name.length() - 1;
746 while (count >= 0) {
747 char c = name.charAt(count);
748 if (!Character.isDigit(c)) break;
749 count--;
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);
761 return 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);
775 else {
776 break;
779 return max;
782 @Nullable
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();
793 return null;
796 @Nullable
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;
824 @Nullable
825 public static PsiType getQualifiedMemberReferenceType(@Nullable PsiType qualifierType, @NotNull final PsiMember member) {
826 final ClassCandidateInfo info = TypeConversionUtil.splitType(qualifierType, member);
827 if (info == null) {
828 return null;
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));
840 return true;
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;
853 @Override
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());
879 return set;
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);
897 set.add(ret);
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);
920 @Nullable
921 public static TailType getDefaultTailTypeForFile(PsiFile file) {
922 return file == null ? null : file.getUserData(COMPLETION_TAIL_TYPE);