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    }