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.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;
30 import java.util.HashSet;
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;
42 private PsiTypeLookupItem(Object o, @NotNull @NonNls String lookupString, boolean diamond, int bracketsCount) {
43 super(o, lookupString);
45 myBracketsCount = bracketsCount;
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);
58 public void setIndicateAnonymous(boolean indicateAnonymous) {
59 myIndicateAnonymous = indicateAnonymous;
62 public boolean isIndicateAnonymous() {
63 return myIndicateAnonymous;
67 public boolean equals(final Object o) {
68 return super.equals(o) && o instanceof PsiTypeLookupItem && getBracketsCount() == ((PsiTypeLookupItem) o).getBracketsCount();
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());
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);
89 editor.getCaretModel().moveToOffset(tail);
91 editor.getScrollingModel().scrollToCaret(ScrollType.RELATIVE);
93 InsertHandler handler = getInsertHandler();
94 if (handler != null && !(handler instanceof DefaultInsertHandler)) {
95 //noinspection unchecked
96 handler.handleInsert(context, this);
100 public String calcGenerics(@NotNull PsiElement context) {
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))) {
117 if (builder.length() > 0) {
118 builder.append(", ");
120 builder.append(substitute.getCanonicalText());
122 if (builder.length() > 0) {
123 return "<" + builder + ">";
130 public int hashCode() {
131 final int fromSuper = super.hashCode();
132 final int dim = getBracketsCount();
133 return fromSuper + dim * 31;
136 public int getBracketsCount() {
137 return myBracketsCount;
140 public static PsiTypeLookupItem createLookupItem(@NotNull PsiType type, @Nullable PsiElement context) {
141 final PsiType original = type;
143 while (type instanceof PsiArrayType) {
144 type = ((PsiArrayType)type).getComponentType();
148 PsiTypeLookupItem item = doCreateItem(type, context, dim);
151 item.setAttribute(TAIL_TEXT_ATTR, " " + StringUtil.repeat("[]", dim));
152 item.setAttribute(TAIL_TEXT_SMALL_ATTR, "");
154 item.setAttribute(TYPE, original);
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();
163 if (psiClass != null) {
164 final PsiSubstitutor substitutor = classResolveResult.getSubstitutor();
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;
174 PsiClass resolved = JavaPsiFacade.getInstance(psiClass.getProject()).getResolveHelper().resolveReferencedClass(psiClass.getName(), context);
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();
189 PsiTypeLookupItem item = new PsiTypeLookupItem(psiClass, lookupString, diamond, bracketsCount);
190 item.addLookupStrings(allStrings.toArray(new String[allStrings.size()]));
191 item.setAttribute(SUBSTITUTOR, substitutor);
196 return new PsiTypeLookupItem(type, type.getPresentableText(), false, bracketsCount);
200 private PsiSubstitutor getSubstitutor() {
201 PsiSubstitutor attribute = (PsiSubstitutor)getAttribute(SUBSTITUTOR);
202 return attribute != null ? attribute : PsiSubstitutor.EMPTY;
206 public void renderElement(LookupElementPresentation presentation) {
207 final Object object = getObject();
208 if (object instanceof PsiClass) {
209 JavaPsiClassReferenceElement.renderClassItem(presentation, this, (PsiClass)object, myDiamond);
211 assert object instanceof PsiType;
213 if (!(object instanceof PsiPrimitiveType)) {
214 presentation.setIcon(DefaultLookupItemRenderer.getRawIcon(this, presentation.isReal()));
217 presentation.setItemText(((PsiType)object).getCanonicalText());
218 presentation.setItemTextBold(getAttribute(LookupItem.HIGHLIGHTED_ATTR) != null || object instanceof PsiPrimitiveType);
220 String tailText = (String)getAttribute(LookupItem.TAIL_TEXT_ATTR);
221 if (tailText != null) {
222 presentation.setTailText(tailText, getAttribute(LookupItem.TAIL_TEXT_SMALL_ATTR) != null);