2 * Copyright 2000-2009 JetBrains s.r.o.
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
8 * http://www.apache.org/licenses/LICENSE-2.0
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.
16 package com.intellij.codeInsight.lookup;
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;
31 import java.util.HashSet;
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;
43 private PsiTypeLookupItem(Object o, @NotNull @NonNls String lookupString, boolean diamond, int bracketsCount) {
44 super(o, lookupString);
46 myBracketsCount = bracketsCount;
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);
59 public void setIndicateAnonymous(boolean indicateAnonymous) {
60 myIndicateAnonymous = indicateAnonymous;
63 public boolean isIndicateAnonymous() {
64 return myIndicateAnonymous;
68 public boolean equals(final Object o) {
69 return super.equals(o) && o instanceof PsiTypeLookupItem && getBracketsCount() == ((PsiTypeLookupItem) o).getBracketsCount();
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());
79 context.getDocument().insertString(context.getTailOffset(), calcGenerics(position));
80 JavaCompletionUtil.shortenReference(context.getFile(), context.getStartOffset());
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);
92 editor.getCaretModel().moveToOffset(tail);
94 editor.getScrollingModel().scrollToCaret(ScrollType.RELATIVE);
96 InsertHandler handler = getInsertHandler();
97 if (handler != null && !(handler instanceof DefaultInsertHandler)) {
98 //noinspection unchecked
99 handler.handleInsert(context, this);
103 public String calcGenerics(@NotNull PsiElement context) {
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))) {
120 if (builder.length() > 0) {
121 builder.append(", ");
123 builder.append(substitute.getCanonicalText());
125 if (builder.length() > 0) {
126 return "<" + builder + ">";
133 public int hashCode() {
134 final int fromSuper = super.hashCode();
135 final int dim = getBracketsCount();
136 return fromSuper + dim * 31;
139 public int getBracketsCount() {
140 return myBracketsCount;
143 public static PsiTypeLookupItem createLookupItem(@NotNull PsiType type, @Nullable PsiElement context) {
144 final PsiType original = type;
146 while (type instanceof PsiArrayType) {
147 type = ((PsiArrayType)type).getComponentType();
151 PsiTypeLookupItem item = doCreateItem(type, context, dim);
154 item.setAttribute(TAIL_TEXT_ATTR, " " + StringUtil.repeat("[]", dim));
155 item.setAttribute(TAIL_TEXT_SMALL_ATTR, "");
157 item.setAttribute(TYPE, original);
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();
166 if (psiClass != null) {
167 final PsiSubstitutor substitutor = classResolveResult.getSubstitutor();
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;
177 PsiClass resolved = JavaPsiFacade.getInstance(psiClass.getProject()).getResolveHelper().resolveReferencedClass(psiClass.getName(), context);
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();
192 PsiTypeLookupItem item = new PsiTypeLookupItem(psiClass, lookupString, diamond, bracketsCount);
193 item.addLookupStrings(allStrings.toArray(new String[allStrings.size()]));
194 item.setAttribute(SUBSTITUTOR, substitutor);
199 return new PsiTypeLookupItem(type, type.getPresentableText(), false, bracketsCount);
203 private PsiSubstitutor getSubstitutor() {
204 PsiSubstitutor attribute = (PsiSubstitutor)getAttribute(SUBSTITUTOR);
205 return attribute != null ? attribute : PsiSubstitutor.EMPTY;
209 public void renderElement(LookupElementPresentation presentation) {
210 final Object object = getObject();
211 if (object instanceof PsiClass) {
212 JavaPsiClassReferenceElement.renderClassItem(presentation, this, (PsiClass)object, myDiamond);
214 assert object instanceof PsiType;
216 if (!(object instanceof PsiPrimitiveType)) {
217 presentation.setIcon(DefaultLookupItemRenderer.getRawIcon(this, presentation.isReal()));
220 presentation.setItemText(((PsiType)object).getCanonicalText());
221 presentation.setItemTextBold(getAttribute(LookupItem.HIGHLIGHTED_ATTR) != null || object instanceof PsiPrimitiveType);
223 String tailText = (String)getAttribute(LookupItem.TAIL_TEXT_ATTR);
224 if (tailText != null) {
225 presentation.setTailText(tailText, getAttribute(LookupItem.TAIL_TEXT_SMALL_ATTR) != null);
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());