blob: 722b9e2816a2960d9e53c8dfd26e3ae97364c432 [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;
import com.intellij.openapi.util.Comparing;
import com.intellij.pom.java.LanguageLevel;
import com.intellij.psi.search.GlobalSearchScope;
import com.intellij.psi.util.PsiUtil;
import com.intellij.util.ArrayFactory;
import org.jetbrains.annotations.NotNull;
import org.jetbrains.annotations.Nullable;
/**
* Represents a class type.
*
* @author max
*/
public abstract class PsiClassType extends PsiType {
/**
* The empty array of PSI class types which can be reused to avoid unnecessary allocations.
*/
public static final PsiClassType[] EMPTY_ARRAY = new PsiClassType[0];
public static final ArrayFactory<PsiClassType> ARRAY_FACTORY = new ArrayFactory<PsiClassType>() {
@NotNull
@Override
public PsiClassType[] create(int count) {
return new PsiClassType[count];
}
};
protected final LanguageLevel myLanguageLevel;
protected PsiClassType(LanguageLevel languageLevel) {
this(languageLevel, PsiAnnotation.EMPTY_ARRAY);
}
protected PsiClassType(LanguageLevel languageLevel, @NotNull PsiAnnotation[] annotations) {
super(annotations);
myLanguageLevel = languageLevel;
}
/**
* Resolves the class reference and returns the resulting class.
*
* @return the class instance, or null if the reference resolve failed.
*/
@Nullable
public abstract PsiClass resolve();
/**
* Returns the non-qualified name of the class referenced by the type.
*
* @return the name of the class.
*/
public abstract String getClassName();
/**
* Returns the list of type arguments for the type.
*
* @return the array of type arguments, or an empty array if the type does not point to a generic class or interface.
*/
@NotNull
public abstract PsiType[] getParameters();
public int getParameterCount() {
return getParameters().length;
}
public boolean equals(Object obj) {
if (this == obj) return true;
if (!(obj instanceof PsiClassType)) return false;
PsiClassType otherClassType = (PsiClassType)obj;
String className = getClassName();
String otherClassName = otherClassType.getClassName();
if (!Comparing.equal(className, otherClassName)) return false;
if (getParameterCount() != otherClassType.getParameterCount()) return false;
final ClassResolveResult result = resolveGenerics();
final ClassResolveResult otherResult = otherClassType.resolveGenerics();
if (result == otherResult) return true;
final PsiClass aClass = result.getElement();
final PsiClass otherClass = otherResult.getElement();
if (aClass == null || otherClass == null) {
return aClass == otherClass;
}
return aClass.getManager().areElementsEquivalent(aClass, otherClass) &&
(PsiUtil.isRawSubstitutor(aClass, result.getSubstitutor()) ||
PsiUtil.equalOnEquivalentClasses(result.getSubstitutor(), aClass, otherResult.getSubstitutor(), otherClass));
}
/**
* Checks if the class type has any parameters with no substituted arguments.
*
* @return true if the class type has non-substituted parameters, false otherwise
*/
public boolean hasParameters() {
final ClassResolveResult resolveResult = resolveGenerics();
PsiClass aClass = resolveResult.getElement();
if (aClass == null) return false;
boolean hasParams = false;
for (PsiTypeParameter parameter : PsiUtil.typeParametersIterable(aClass)) {
if (resolveResult.getSubstitutor().substitute(parameter) == null) return false;
hasParams = true;
}
return hasParams;
}
/**
* Checks if the class type has any parameters which are not unbounded wildcards (and not extends-wildcard with the same bound as corresponding type parameter bound)
* and do not have substituted arguments.
*
* @return true if the class type has nontrivial non-substituted parameters, false otherwise
*/
public boolean hasNonTrivialParameters() {
final ClassResolveResult resolveResult = resolveGenerics();
PsiClass aClass = resolveResult.getElement();
if (aClass == null) return false;
for (PsiTypeParameter parameter : PsiUtil.typeParametersIterable(aClass)) {
PsiType type = resolveResult.getSubstitutor().substitute(parameter);
if (type != null) {
if (!(type instanceof PsiWildcardType)) {
return true;
}
final PsiType bound = ((PsiWildcardType)type).getBound();
if (bound != null) {
if (((PsiWildcardType)type).isExtends()) {
final PsiClass superClass = parameter.getSuperClass();
if (superClass != null && PsiUtil.resolveClassInType(bound) == superClass) {
continue;
}
}
return true;
}
}
}
return false;
}
public int hashCode() {
final String className = getClassName();
if (className == null) return 0;
return className.hashCode();
}
@Override
@NotNull
public PsiType[] getSuperTypes() {
ClassResolveResult resolveResult = resolveGenerics();
PsiClass aClass = resolveResult.getElement();
if (aClass == null) return EMPTY_ARRAY;
PsiClassType[] superTypes = aClass.getSuperTypes();
PsiType[] substitutionResults = createArray(superTypes.length);
for (int i = 0; i < superTypes.length; i++) {
substitutionResults[i] = resolveResult.getSubstitutor().substitute(superTypes[i]);
}
return substitutionResults;
}
/**
* Checks whether the specified resolve result represents a raw type. <br>
* Raw type is a class type for a class <i>with type parameters</i> which does not assign
* any value to them. If a class does not have any type parameters, it cannot generate any raw type.
*/
public static boolean isRaw(ClassResolveResult resolveResult) {
PsiClass psiClass = resolveResult.getElement();
return psiClass != null && PsiUtil.isRawSubstitutor(psiClass, resolveResult.getSubstitutor());
}
/**
* Checks whether this type is a raw type. <br>
* Raw type is a class type for a class <i>with type parameters</i> which does not assign
* any value to them. If a class does not have any type parameters, it cannot generate any raw type.
*/
public boolean isRaw() {
return isRaw(resolveGenerics());
}
/**
* Returns the resolve result containing the class referenced by the class type and the
* substitutor which can substitute the values of type arguments used in the class type
* declaration.
*
* @return the resolve result instance.
*/
@NotNull
public abstract ClassResolveResult resolveGenerics();
/**
* Returns the raw type (with no values assigned to type parameters) corresponding to this type.
*
* @return the raw type instance.
*/
@NotNull
public abstract PsiClassType rawType();
/**
* Overrides {@link com.intellij.psi.PsiType#getResolveScope()} to narrow specify @NotNull.
*/
@Override
@NotNull
public abstract GlobalSearchScope getResolveScope();
@Override
public <A> A accept(@NotNull PsiTypeVisitor<A> visitor) {
return visitor.visitClassType(this);
}
@NotNull
public abstract LanguageLevel getLanguageLevel();
/**
* Functional style setter preserving original type's language level
*
* @param languageLevel level to obtain class type with
* @return type with requested language level
*/
@NotNull
public abstract PsiClassType setLanguageLevel(@NotNull LanguageLevel languageLevel);
/**
* Represents the result of resolving a reference to a Java class.
*/
public interface ClassResolveResult extends JavaResolveResult {
@Override
PsiClass getElement();
ClassResolveResult EMPTY = new ClassResolveResult() {
@Override
public PsiClass getElement() {
return null;
}
@NotNull
@Override
public PsiSubstitutor getSubstitutor() {
return PsiSubstitutor.EMPTY;
}
@Override
public boolean isValidResult() {
return false;
}
@Override
public boolean isAccessible() {
return false;
}
@Override
public boolean isStaticsScopeCorrect() {
return false;
}
@Override
public PsiElement getCurrentFileResolveScope() {
return null;
}
@Override
public boolean isPackagePrefixPackageReference() {
return false;
}
};
}
/**
* Temporary class to facilitate transition to {@link #getCanonicalText(boolean)}.
*/
public static abstract class Stub extends PsiClassType {
protected Stub(LanguageLevel languageLevel, @NotNull PsiAnnotation[] annotations) {
super(languageLevel, annotations);
}
@NotNull
@Override
public final String getCanonicalText() {
return getCanonicalText(false);
}
@NotNull
@Override
public abstract String getCanonicalText(boolean annotated);
}
}