blob: fe81930399985f899f1c8870f5fa701321ca13f0 [file] [log] [blame]
/*
* Copyright 2000-2014 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.psi.impl.source;
import com.intellij.openapi.util.text.StringUtil;
import com.intellij.pom.java.LanguageLevel;
import com.intellij.psi.*;
import com.intellij.psi.search.GlobalSearchScope;
import com.intellij.psi.util.PsiUtil;
import com.intellij.psi.util.PsiUtilCore;
import com.intellij.util.IncorrectOperationException;
import org.jetbrains.annotations.NotNull;
import org.jetbrains.annotations.Nullable;
import java.util.ArrayList;
import java.util.List;
/**
* @author dsl
*/
public class PsiImmediateClassType extends PsiClassType.Stub {
private final PsiClass myClass;
private final PsiSubstitutor mySubstitutor;
private final PsiManager myManager;
private String myCanonicalText;
private String myCanonicalTextAnnotated;
private String myPresentableText;
private String myInternalCanonicalText;
private final ClassResolveResult myClassResolveResult = new ClassResolveResult() {
@Override
public PsiClass getElement() {
return myClass;
}
@NotNull
@Override
public PsiSubstitutor getSubstitutor() {
return mySubstitutor;
}
@Override
public boolean isValidResult() {
return true;
}
@Override
public boolean isAccessible() {
return true;
}
@Override
public boolean isStaticsScopeCorrect() {
return true;
}
@Override
public PsiElement getCurrentFileResolveScope() {
return null;
}
@Override
public boolean isPackagePrefixPackageReference() {
return false;
}
};
public PsiImmediateClassType(@NotNull PsiClass aClass, @NotNull PsiSubstitutor substitutor) {
this(aClass, substitutor, null, PsiAnnotation.EMPTY_ARRAY);
}
public PsiImmediateClassType(@NotNull PsiClass aClass, @NotNull PsiSubstitutor substitutor, @Nullable LanguageLevel level) {
this(aClass, substitutor, level, PsiAnnotation.EMPTY_ARRAY);
}
public PsiImmediateClassType(@NotNull PsiClass aClass,
@NotNull PsiSubstitutor substitutor,
@Nullable LanguageLevel level,
@NotNull PsiAnnotation... annotations) {
super(level, annotations);
myClass = aClass;
myManager = aClass.getManager();
mySubstitutor = substitutor;
assert substitutor.isValid();
}
@Override
public PsiClass resolve() {
return myClass;
}
@Override
public String getClassName() {
return myClass.getName();
}
@Override
@NotNull
public PsiType[] getParameters() {
final PsiTypeParameter[] parameters = myClass.getTypeParameters();
if (parameters.length == 0) {
return PsiType.EMPTY_ARRAY;
}
List<PsiType> lst = new ArrayList<PsiType>();
for (PsiTypeParameter parameter : parameters) {
PsiType substituted = mySubstitutor.substitute(parameter);
if (substituted != null) {
lst.add(substituted);
}
}
return lst.toArray(createArray(lst.size()));
}
@Override
@NotNull
public ClassResolveResult resolveGenerics() {
return myClassResolveResult;
}
@Override
@NotNull
public PsiClassType rawType() {
return JavaPsiFacade.getInstance(myClass.getProject()).getElementFactory().createType(myClass);
}
@NotNull
@Override
public String getPresentableText() {
if (myPresentableText == null) {
myPresentableText = getText(TextType.PRESENTABLE, true);
}
return myPresentableText;
}
@NotNull
@Override
public String getCanonicalText(boolean annotated) {
String cached = annotated ? myCanonicalTextAnnotated : myCanonicalText;
if (cached == null) {
cached = getText(TextType.CANONICAL, annotated);
if (annotated) myCanonicalTextAnnotated = cached;
else myCanonicalText = cached;
}
return cached;
}
@NotNull
@Override
public String getInternalCanonicalText() {
if (myInternalCanonicalText == null) {
myInternalCanonicalText = getText(TextType.INT_CANONICAL, true);
}
return myInternalCanonicalText;
}
private enum TextType { PRESENTABLE, CANONICAL, INT_CANONICAL }
private String getText(@NotNull TextType textType, boolean annotated) {
mySubstitutor.ensureValid();
StringBuilder buffer = new StringBuilder();
buildText(myClass, mySubstitutor, buffer, textType, annotated);
return buffer.toString();
}
private void buildText(@NotNull PsiClass aClass,
@NotNull PsiSubstitutor substitutor,
@NotNull StringBuilder buffer,
@NotNull TextType textType,
boolean annotated) {
if (aClass instanceof PsiAnonymousClass) {
ClassResolveResult baseResolveResult = ((PsiAnonymousClass)aClass).getBaseClassType().resolveGenerics();
PsiClass baseClass = baseResolveResult.getElement();
if (baseClass != null) {
if (textType == TextType.INT_CANONICAL) {
buffer.append("anonymous ");
}
buildText(baseClass, baseResolveResult.getSubstitutor(), buffer, textType, false);
}
return;
}
boolean qualified = textType != TextType.PRESENTABLE;
PsiClass enclosingClass = null;
if (!aClass.hasModifierProperty(PsiModifier.STATIC)) {
PsiElement parent = aClass.getParent();
if (parent instanceof PsiClass && !(parent instanceof PsiAnonymousClass)) {
enclosingClass = (PsiClass)parent;
}
}
if (enclosingClass != null) {
buildText(enclosingClass, substitutor, buffer, textType, false);
buffer.append('.');
}
else if (qualified) {
String fqn = aClass.getQualifiedName();
if (fqn != null) {
String prefix = StringUtil.getPackageName(fqn);
if (!StringUtil.isEmpty(prefix)) {
buffer.append(prefix);
buffer.append('.');
}
}
}
if (annotated) {
PsiNameHelper.appendAnnotations(buffer, getAnnotations(), qualified);
}
buffer.append(aClass.getName());
PsiTypeParameter[] typeParameters = aClass.getTypeParameters();
if (typeParameters.length > 0) {
int pos = buffer.length();
buffer.append('<');
for (int i = 0; i < typeParameters.length; i++) {
PsiTypeParameter typeParameter = typeParameters[i];
PsiUtilCore.ensureValid(typeParameter);
if (i > 0) {
buffer.append(',');
if (textType == TextType.PRESENTABLE) buffer.append(' ');
}
PsiType substitutionResult = substitutor.substitute(typeParameter);
if (substitutionResult == null) {
buffer.setLength(pos);
pos = -1;
break;
}
PsiUtil.ensureValidType(substitutionResult);
if (textType == TextType.PRESENTABLE) {
buffer.append(substitutionResult.getPresentableText());
}
else if (textType == TextType.CANONICAL) {
buffer.append(substitutionResult.getCanonicalText(annotated));
}
else {
buffer.append(substitutionResult.getInternalCanonicalText());
}
}
if (pos >= 0) {
buffer.append('>');
}
}
}
@Override
public boolean isValid() {
return myClass.isValid() && mySubstitutor.isValid();
}
@Override
public boolean equalsToText(@NotNull String text) {
PsiElementFactory factory = JavaPsiFacade.getInstance(myManager.getProject()).getElementFactory();
final PsiType patternType;
try {
patternType = factory.createTypeFromText(text, myClass);
}
catch (IncorrectOperationException e) {
return false;
}
return equals(patternType);
}
@Override
@NotNull
public GlobalSearchScope getResolveScope() {
return myClass.getResolveScope();
}
@Override
@NotNull
public LanguageLevel getLanguageLevel() {
return myLanguageLevel != null ? myLanguageLevel : PsiUtil.getLanguageLevel(myClass);
}
@NotNull
@Override
public PsiClassType setLanguageLevel(@NotNull LanguageLevel level) {
return level.equals(myLanguageLevel) ? this : new PsiImmediateClassType(myClass, mySubstitutor, level, getAnnotations());
}
}