blob: 9934cddc90ff8c9b5ada7285c9a956571ba2ff7a [file] [log] [blame]
/*
* Copyright 2000-2013 JetBrains s.r.o.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package com.intellij.codeInsight.completion;
import com.intellij.codeInsight.TailType;
import com.intellij.codeInsight.lookup.DefaultLookupItemRenderer;
import com.intellij.codeInsight.lookup.LookupElementPresentation;
import com.intellij.codeInsight.lookup.LookupItem;
import com.intellij.codeInsight.lookup.PsiTypeLookupItem;
import com.intellij.codeInsight.lookup.impl.JavaElementLookupRenderer;
import com.intellij.openapi.util.ClassConditionKey;
import com.intellij.openapi.util.Comparing;
import com.intellij.openapi.util.Key;
import com.intellij.openapi.util.text.StringUtil;
import com.intellij.psi.*;
import com.intellij.psi.codeStyle.CodeStyleSettingsManager;
import com.intellij.psi.util.PsiUtilCore;
import com.intellij.reference.SoftReference;
import com.intellij.util.Function;
import org.jetbrains.annotations.NotNull;
import org.jetbrains.annotations.Nullable;
import java.lang.ref.Reference;
import java.lang.ref.WeakReference;
import java.util.Collections;
import java.util.Set;
/**
* @author peter
*/
public class JavaPsiClassReferenceElement extends LookupItem<Object> {
public static final Key<String> PACKAGE_NAME = Key.create("PACKAGE_NAME");
public static final ClassConditionKey<JavaPsiClassReferenceElement> CLASS_CONDITION_KEY = ClassConditionKey.create(JavaPsiClassReferenceElement.class);
private final Object myClass;
private volatile Reference<PsiClass> myCache;
private final String myQualifiedName;
private String myForcedPresentableName;
public JavaPsiClassReferenceElement(PsiClass psiClass) {
super(psiClass.getName(), psiClass.getName());
myQualifiedName = psiClass.getQualifiedName();
myClass = psiClass.getContainingFile().getVirtualFile() == null || myQualifiedName == null ? psiClass : PsiAnchor.create(psiClass);
JavaCompletionUtil.setShowFQN(this);
setInsertHandler(AllClassesGetter.TRY_SHORTENING);
setTailType(TailType.NONE);
}
public String getForcedPresentableName() {
return myForcedPresentableName;
}
@NotNull
@Override
public String getLookupString() {
if (myForcedPresentableName != null) {
return myForcedPresentableName;
}
return super.getLookupString();
}
@Override
public Set<String> getAllLookupStrings() {
if (myForcedPresentableName != null) {
return Collections.singleton(myForcedPresentableName);
}
return super.getAllLookupStrings();
}
public void setForcedPresentableName(String forcedPresentableName) {
myForcedPresentableName = forcedPresentableName;
}
@NotNull
@Override
public PsiClass getObject() {
if (myClass instanceof PsiAnchor) {
PsiClass psiClass = SoftReference.dereference(myCache);
if (psiClass != null) {
return psiClass;
}
final PsiClass retrieve = (PsiClass)((PsiAnchor)myClass).retrieve();
assert retrieve != null : myQualifiedName;
myCache = new WeakReference<PsiClass>(retrieve);
return retrieve;
}
return (PsiClass)myClass;
}
@Override
public boolean isValid() {
if (myClass instanceof PsiClass) {
return ((PsiClass)myClass).isValid();
}
return ((PsiAnchor)myClass).retrieve() != null;
}
@Override
public boolean equals(final Object o) {
if (this == o) return true;
if (!(o instanceof JavaPsiClassReferenceElement)) return false;
final JavaPsiClassReferenceElement that = (JavaPsiClassReferenceElement)o;
if (myQualifiedName != null) {
return myQualifiedName.equals(that.myQualifiedName);
}
return Comparing.equal(myClass, that.myClass);
}
public String getQualifiedName() {
return myQualifiedName;
}
@Override
public int hashCode() {
final String s = myQualifiedName;
return s == null ? 239 : s.hashCode();
}
@Override
public void renderElement(LookupElementPresentation presentation) {
LookupItem item = this;
PsiClass psiClass = getObject();
renderClassItem(presentation, item, psiClass, false);
}
public static void renderClassItem(LookupElementPresentation presentation, LookupItem item, PsiClass psiClass, boolean diamond) {
if (!(psiClass instanceof PsiTypeParameter)) {
presentation.setIcon(DefaultLookupItemRenderer.getRawIcon(item, presentation.isReal()));
}
final boolean bold = item.getAttribute(LookupItem.HIGHLIGHTED_ATTR) != null;
boolean strikeout = JavaElementLookupRenderer.isToStrikeout(item);
presentation.setItemText(getName(psiClass, item, diamond));
presentation.setStrikeout(strikeout);
presentation.setItemTextBold(bold);
String tailText = getLocationString(item);
PsiSubstitutor substitutor = (PsiSubstitutor)item.getAttribute(LookupItem.SUBSTITUTOR);
if (item instanceof PsiTypeLookupItem) {
if (((PsiTypeLookupItem)item).isIndicateAnonymous() &&
(psiClass.isInterface() || psiClass.hasModifierProperty(PsiModifier.ABSTRACT)) ||
((PsiTypeLookupItem)item).isAddArrayInitializer()) {
tailText = "{...}" + tailText;
}
}
if (substitutor == null && !diamond && psiClass.getTypeParameters().length > 0) {
tailText = "<" + StringUtil.join(psiClass.getTypeParameters(), new Function<PsiTypeParameter, String>() {
@Override
public String fun(PsiTypeParameter psiTypeParameter) {
return psiTypeParameter.getName();
}
}, "," + (showSpaceAfterComma(psiClass) ? " " : "")) + ">" + tailText;
}
presentation.setTailText(tailText, true);
}
public static String getLocationString(LookupItem<?> item) {
String pkgName = item.getAttribute(PACKAGE_NAME);
return pkgName == null ? "" : " (" + pkgName + ")";
}
private static String getName(final PsiClass psiClass, final LookupItem<?> item, boolean diamond) {
if (item instanceof JavaPsiClassReferenceElement) {
String forced = ((JavaPsiClassReferenceElement)item).getForcedPresentableName();
if (forced != null) {
return forced;
}
}
String name = PsiUtilCore.getName(psiClass);
if (item.getAttribute(LookupItem.FORCE_QUALIFY) != null) {
if (psiClass.getContainingClass() != null) {
name = psiClass.getContainingClass().getName() + "." + name;
}
}
if (diamond) {
return name + "<>";
}
PsiSubstitutor substitutor = (PsiSubstitutor)item.getAttribute(LookupItem.SUBSTITUTOR);
if (substitutor != null) {
final PsiTypeParameter[] params = psiClass.getTypeParameters();
if (params.length > 0) {
return name + formatTypeParameters(substitutor, params);
}
}
return StringUtil.notNullize(name);
}
@Nullable
private static String formatTypeParameters(@NotNull final PsiSubstitutor substitutor, final PsiTypeParameter[] params) {
final boolean space = showSpaceAfterComma(params[0]);
StringBuilder buffer = new StringBuilder();
buffer.append("<");
for(int i = 0; i < params.length; i++){
final PsiTypeParameter param = params[i];
final PsiType type = substitutor.substitute(param);
if(type == null){
return "";
}
if (type instanceof PsiClassType && ((PsiClassType)type).getParameters().length > 0) {
buffer.append(((PsiClassType)type).rawType().getPresentableText()).append("<...>");
} else {
buffer.append(type.getPresentableText());
}
if(i < params.length - 1) {
buffer.append(",");
if (space) {
buffer.append(" ");
}
}
}
buffer.append(">");
return buffer.toString();
}
private static boolean showSpaceAfterComma(PsiClass element) {
return CodeStyleSettingsManager.getSettings(element.getProject()).SPACE_AFTER_COMMA;
}
}