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