001 /*
002 * Copyright 2010-2015 JetBrains s.r.o.
003 *
004 * Licensed under the Apache License, Version 2.0 (the "License");
005 * you may not use this file except in compliance with the License.
006 * You may obtain a copy of the License at
007 *
008 * http://www.apache.org/licenses/LICENSE-2.0
009 *
010 * Unless required by applicable law or agreed to in writing, software
011 * distributed under the License is distributed on an "AS IS" BASIS,
012 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
013 * See the License for the specific language governing permissions and
014 * limitations under the License.
015 */
016
017 package org.jetbrains.kotlin.load.java.structure.impl;
018
019 import com.intellij.psi.*;
020 import org.jetbrains.annotations.NotNull;
021 import org.jetbrains.annotations.Nullable;
022 import org.jetbrains.kotlin.load.java.structure.JavaClassifierType;
023 import org.jetbrains.kotlin.load.java.structure.JavaType;
024
025 import java.util.ArrayList;
026 import java.util.Collections;
027 import java.util.List;
028
029 public class JavaClassifierTypeImpl extends JavaTypeImpl<PsiClassType> implements JavaClassifierType {
030 private static class ResolutionResult {
031 private final JavaClassifierImpl<?> classifier;
032 private final PsiSubstitutor substitutor;
033 private final boolean isRaw;
034
035 private ResolutionResult(@Nullable JavaClassifierImpl<?> classifier, @NotNull PsiSubstitutor substitutor, boolean isRaw) {
036 this.classifier = classifier;
037 this.substitutor = substitutor;
038 this.isRaw = isRaw;
039 }
040 }
041
042 private ResolutionResult resolutionResult;
043
044 public JavaClassifierTypeImpl(@NotNull PsiClassType psiClassType) {
045 super(psiClassType);
046 }
047
048 @Override
049 @Nullable
050 public JavaClassifierImpl<?> getClassifier() {
051 resolve();
052 return resolutionResult.classifier;
053 }
054
055 @NotNull
056 public PsiSubstitutor getSubstitutor() {
057 resolve();
058 return resolutionResult.substitutor;
059 }
060
061 private void resolve() {
062 if (resolutionResult == null) {
063 PsiClassType.ClassResolveResult result = getPsi().resolveGenerics();
064 PsiClass psiClass = result.getElement();
065 PsiSubstitutor substitutor = result.getSubstitutor();
066 resolutionResult = new ResolutionResult(
067 psiClass == null ? null : JavaClassifierImpl.create(psiClass), substitutor, PsiClassType.isRaw(result)
068 );
069 }
070 }
071
072 @Override
073 @NotNull
074 public String getCanonicalText() {
075 return getPsi().getCanonicalText();
076 }
077
078 @Override
079 @NotNull
080 public String getPresentableText() {
081 return getPsi().getPresentableText();
082 }
083
084 @Override
085 public boolean isRaw() {
086 resolve();
087 return resolutionResult.isRaw;
088 }
089
090 @Override
091 @NotNull
092 public List<JavaType> getTypeArguments() {
093 JavaClassifierImpl<?> classifier = getClassifier();
094 if (!(classifier instanceof JavaClassImpl)) return Collections.emptyList();
095
096 // parameters including ones from outer class
097 List<PsiTypeParameter> parameters = getTypeParameters(classifier.getPsi());
098
099 PsiSubstitutor substitutor = getSubstitutor();
100
101 List<JavaType> result = new ArrayList<JavaType>(parameters.size());
102 for (PsiTypeParameter typeParameter : parameters) {
103 PsiType substitutedType = substitutor.substitute(typeParameter);
104 result.add(substitutedType == null ? null : JavaTypeImpl.create(substitutedType));
105 }
106
107 return result;
108 }
109
110 // Copy-pasted from PsiUtil.typeParametersIterable
111 // The only change is using `Collections.addAll(result, typeParameters)` instead of reversing type parameters of `currentOwner`
112 // Result differs in cases like:
113 // class Outer<H1> {
114 // class Inner<H2, H3> {}
115 // }
116 //
117 // PsiUtil.typeParametersIterable returns H3, H2, H1
118 // But we would like to have H2, H3, H1 as such order is consistent with our type representation
119 @NotNull
120 private static List<PsiTypeParameter> getTypeParameters(@NotNull PsiClass owner) {
121 List<PsiTypeParameter> result = null;
122
123 PsiTypeParameterListOwner currentOwner = owner;
124 while (currentOwner != null) {
125 PsiTypeParameter[] typeParameters = currentOwner.getTypeParameters();
126 if (typeParameters.length > 0) {
127 if (result == null) result = new ArrayList<PsiTypeParameter>(typeParameters.length);
128 Collections.addAll(result, typeParameters);
129 }
130
131 if (currentOwner.hasModifierProperty(PsiModifier.STATIC)) break;
132 currentOwner = currentOwner.getContainingClass();
133 }
134
135 if (result == null) return Collections.emptyList();
136 return result;
137 }
138 }