47fca85f1d6ad3d0e4bae4486eed0380ca00fbac
[idea/community.git] / java / java-impl / src / com / intellij / codeInsight / lookup / PsiTypeLookupItem.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.lookup;
17
18 import com.intellij.codeInsight.completion.*;
19 import com.intellij.openapi.editor.Editor;
20 import com.intellij.openapi.editor.ScrollType;
21 import com.intellij.openapi.util.ClassConditionKey;
22 import com.intellij.openapi.util.text.StringUtil;
23 import com.intellij.psi.*;
24 import com.intellij.psi.impl.source.PsiClassReferenceType;
25 import com.intellij.psi.util.PsiUtil;
26 import org.jetbrains.annotations.NonNls;
27 import org.jetbrains.annotations.NotNull;
28 import org.jetbrains.annotations.Nullable;
29
30 import java.util.HashSet;
31 import java.util.Set;
32
33 /**
34  * @author peter
35  */
36 public class PsiTypeLookupItem extends LookupItem {
37   public static final ClassConditionKey<PsiTypeLookupItem> CLASS_CONDITION_KEY = ClassConditionKey.create(PsiTypeLookupItem.class);
38   private final boolean myDiamond;
39   private final int myBracketsCount;
40   private boolean myIndicateAnonymous;
41
42   private PsiTypeLookupItem(Object o, @NotNull @NonNls String lookupString, boolean diamond, int bracketsCount) {
43     super(o, lookupString);
44     myDiamond = diamond;
45     myBracketsCount = bracketsCount;
46   }
47
48   public PsiType getPsiType() {
49     Object object = getObject();
50     PsiType type = object instanceof PsiType ? (PsiType)object : JavaPsiFacade.getElementFactory(((PsiClass) object).getProject()).createType((PsiClass)object);
51     for (int i = 0; i < getBracketsCount(); i++) {
52       type = new PsiArrayType(type);
53     }
54     return type;
55   }
56
57
58   public void setIndicateAnonymous(boolean indicateAnonymous) {
59     myIndicateAnonymous = indicateAnonymous;
60   }
61
62   public boolean isIndicateAnonymous() {
63     return myIndicateAnonymous;
64   }
65
66   @Override
67   public boolean equals(final Object o) {
68     return super.equals(o) && o instanceof PsiTypeLookupItem && getBracketsCount() == ((PsiTypeLookupItem) o).getBracketsCount();
69   }
70
71   @Override
72   public void handleInsert(InsertionContext context) {
73     PsiElement position = context.getFile().findElementAt(context.getStartOffset());
74     assert position != null;
75     DefaultInsertHandler.addImportForItem(context, this);
76     context.getDocument().insertString(context.getTailOffset(), calcGenerics(position));
77     JavaCompletionUtil.shortenReference(context.getFile(), context.getStartOffset());
78
79     int tail = context.getTailOffset();
80     String braces = StringUtil.repeat("[]", getBracketsCount());
81     Editor editor = context.getEditor();
82     if (!braces.isEmpty()) {
83       context.getDocument().insertString(tail, braces);
84       editor.getCaretModel().moveToOffset(tail + 1);
85       if (context.getCompletionChar() == '[') {
86         context.setAddCompletionChar(false);
87       }
88     } else {
89       editor.getCaretModel().moveToOffset(tail);
90     }
91     editor.getScrollingModel().scrollToCaret(ScrollType.RELATIVE);
92
93     InsertHandler handler = getInsertHandler();
94     if (handler != null && !(handler instanceof DefaultInsertHandler)) {
95       //noinspection unchecked
96       handler.handleInsert(context, this);
97     }
98   }
99
100   public String calcGenerics(@NotNull PsiElement context) {
101     if (myDiamond) {
102       return "<>";
103     }
104
105     if (getObject() instanceof PsiClass) {
106       PsiClass psiClass = (PsiClass)getObject();
107       PsiResolveHelper resolveHelper = JavaPsiFacade.getInstance(psiClass.getProject()).getResolveHelper();
108       PsiSubstitutor substitutor = getSubstitutor();
109       StringBuilder builder = new StringBuilder();
110       for (PsiTypeParameter parameter : psiClass.getTypeParameters()) {
111         PsiType substitute = substitutor.substitute(parameter);
112         if (substitute == null ||
113             (PsiUtil.resolveClassInType(substitute) == parameter && 
114              resolveHelper.resolveReferencedClass(parameter.getName(), context) != CompletionUtil.getOriginalOrSelf(parameter))) {
115           return "";
116         }
117         if (builder.length() > 0) {
118           builder.append(", ");
119         }
120         builder.append(substitute.getCanonicalText());
121       }
122       if (builder.length() > 0) {
123         return "<" + builder + ">";
124       }
125     }
126     return "";
127   }
128
129   @Override
130   public int hashCode() {
131     final int fromSuper = super.hashCode();
132     final int dim = getBracketsCount();
133     return fromSuper + dim * 31;
134   }
135
136   public int getBracketsCount() {
137     return myBracketsCount;
138   }
139
140   public static PsiTypeLookupItem createLookupItem(@NotNull PsiType type, @Nullable PsiElement context) {
141     final PsiType original = type;
142     int dim = 0;
143     while (type instanceof PsiArrayType) {
144       type = ((PsiArrayType)type).getComponentType();
145       dim++;
146     }
147
148     PsiTypeLookupItem item = doCreateItem(type, context, dim);
149
150     if (dim > 0) {
151       item.setAttribute(TAIL_TEXT_ATTR, " " + StringUtil.repeat("[]", dim));
152       item.setAttribute(TAIL_TEXT_SMALL_ATTR, "");
153     }
154     item.setAttribute(TYPE, original);
155     return item;
156   }
157
158   private static PsiTypeLookupItem doCreateItem(final PsiType type, PsiElement context, int bracketsCount) {
159     if (type instanceof PsiClassType) {
160       PsiClassType.ClassResolveResult classResolveResult = ((PsiClassType)type).resolveGenerics();
161       final PsiClass psiClass = classResolveResult.getElement();
162
163       if (psiClass != null) {
164         final PsiSubstitutor substitutor = classResolveResult.getSubstitutor();
165
166         boolean diamond = false;
167         if (type instanceof PsiClassReferenceType) {
168           final PsiReferenceParameterList parameterList = ((PsiClassReferenceType)type).getReference().getParameterList();
169           if (parameterList != null) {
170             final PsiTypeElement[] typeParameterElements = parameterList.getTypeParameterElements();
171             diamond = typeParameterElements.length == 1 && typeParameterElements[0].getType() instanceof PsiDiamondType;
172           }
173         }
174         PsiClass resolved = JavaPsiFacade.getInstance(psiClass.getProject()).getResolveHelper().resolveReferencedClass(psiClass.getName(), context);
175
176         Set<String> allStrings = new HashSet<String>();
177         String lookupString = psiClass.getName();
178         allStrings.add(lookupString);
179         if (!psiClass.getManager().areElementsEquivalent(resolved, psiClass)) {
180           // inner class name should be shown qualified if its not accessible by single name
181           PsiClass aClass = psiClass.getContainingClass();
182           while (aClass != null) {
183             lookupString = aClass.getName() + '.' + lookupString;
184             allStrings.add(lookupString);
185             aClass = aClass.getContainingClass();
186           }
187         }
188
189         PsiTypeLookupItem item = new PsiTypeLookupItem(psiClass, lookupString, diamond, bracketsCount);
190         item.addLookupStrings(allStrings.toArray(new String[allStrings.size()]));
191         item.setAttribute(SUBSTITUTOR, substitutor);
192         return item;
193       }
194
195     }
196     return new PsiTypeLookupItem(type, type.getPresentableText(), false, bracketsCount);
197   }
198
199   @NotNull
200   private PsiSubstitutor getSubstitutor() {
201     PsiSubstitutor attribute = (PsiSubstitutor)getAttribute(SUBSTITUTOR);
202     return attribute != null ? attribute : PsiSubstitutor.EMPTY;
203   }
204
205   @Override
206   public void renderElement(LookupElementPresentation presentation) {
207     final Object object = getObject();
208     if (object instanceof PsiClass) {
209       JavaPsiClassReferenceElement.renderClassItem(presentation, this, (PsiClass)object, myDiamond);
210     } else {
211       assert object instanceof PsiType;
212
213       if (!(object instanceof PsiPrimitiveType)) {
214         presentation.setIcon(DefaultLookupItemRenderer.getRawIcon(this, presentation.isReal()));
215       }
216
217       presentation.setItemText(((PsiType)object).getCanonicalText());
218       presentation.setItemTextBold(getAttribute(LookupItem.HIGHLIGHTED_ATTR) != null || object instanceof PsiPrimitiveType);
219
220       String tailText = (String)getAttribute(LookupItem.TAIL_TEXT_ATTR);
221       if (tailText != null) {
222         presentation.setTailText(tailText, getAttribute(LookupItem.TAIL_TEXT_SMALL_ATTR) != null);
223       }
224     }
225   }
226 }