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 com.intellij.psi.util.PsiUtil;
021    import org.jetbrains.annotations.NotNull;
022    import org.jetbrains.annotations.Nullable;
023    import org.jetbrains.kotlin.load.java.structure.*;
024    
025    import java.util.*;
026    
027    public class JavaClassifierTypeImpl extends JavaTypeImpl<PsiClassType> implements JavaClassifierType {
028        private static class ResolutionResult {
029            private final JavaClassifier classifier;
030            private final JavaTypeSubstitutor substitutor;
031    
032            private ResolutionResult(@Nullable JavaClassifier classifier, @NotNull JavaTypeSubstitutor substitutor) {
033                this.classifier = classifier;
034                this.substitutor = substitutor;
035            }
036        }
037    
038        private ResolutionResult resolutionResult;
039    
040        public JavaClassifierTypeImpl(@NotNull PsiClassType psiClassType) {
041            super(psiClassType);
042        }
043    
044        @Override
045        @Nullable
046        public JavaClassifier getClassifier() {
047            resolve();
048            return resolutionResult.classifier;
049        }
050    
051        @Override
052        @NotNull
053        public JavaTypeSubstitutor getSubstitutor() {
054            resolve();
055            return resolutionResult.substitutor;
056        }
057    
058        private void resolve() {
059            if (resolutionResult == null) {
060                PsiClassType.ClassResolveResult result = getPsi().resolveGenerics();
061                PsiClass psiClass = result.getElement();
062                PsiSubstitutor substitutor = result.getSubstitutor();
063                resolutionResult = new ResolutionResult(
064                        psiClass == null ? null : JavaClassifierImpl.create(psiClass),
065                        new JavaTypeSubstitutorImpl(convertSubstitutionMap(substitutor.getSubstitutionMap()))
066                );
067            }
068        }
069    
070        @NotNull
071        private static Map<JavaTypeParameter, JavaType> convertSubstitutionMap(@NotNull Map<PsiTypeParameter, PsiType> psiMap) {
072            if (psiMap.isEmpty()) return Collections.emptyMap();
073    
074            Map<JavaTypeParameter, JavaType> substitutionMap = new HashMap<JavaTypeParameter, JavaType>();
075            for (Map.Entry<PsiTypeParameter, PsiType> entry : psiMap.entrySet()) {
076                PsiType value = entry.getValue();
077                substitutionMap.put(new JavaTypeParameterImpl(entry.getKey()), value == null ? null : JavaTypeImpl.create(value));
078            }
079    
080            return substitutionMap;
081        }
082    
083        @Override
084        @NotNull
085        public Collection<JavaClassifierType> getSupertypes() {
086            PsiType[] psiTypes = getPsi().getSuperTypes();
087            if (psiTypes.length == 0) return Collections.emptyList();
088            List<JavaClassifierType> result = new ArrayList<JavaClassifierType>(psiTypes.length);
089            for (PsiType psiType : psiTypes) {
090                if (!(psiType instanceof PsiClassType)) {
091                    throw new IllegalStateException("Supertype should be a class: " + psiType + ", type: " + getPsi());
092                }
093                result.add(new JavaClassifierTypeImpl((PsiClassType) psiType));
094            }
095            return result;
096        }
097    
098        @Override
099        @NotNull
100        public String getPresentableText() {
101            return getPsi().getPresentableText();
102        }
103    
104        @Override
105        public boolean isRaw() {
106            return getPsi().isRaw();
107        }
108    
109        @Override
110        @NotNull
111        public List<JavaType> getTypeArguments() {
112            JavaClassifier classifier = getClassifier();
113    
114            // parameters including ones from outer class
115            Iterable<PsiTypeParameter> parameters = classifier instanceof JavaClassImpl
116                         ? getReversedTypeParameters((JavaClassImpl) classifier)
117                         : Collections.<PsiTypeParameter>emptyList();
118    
119            JavaTypeSubstitutor substitutor = getSubstitutor();
120    
121            List<JavaType> result = new ArrayList<JavaType>();
122            for (PsiTypeParameter typeParameter : parameters) {
123                result.add(substitutor.substitute(new JavaTypeParameterImpl(typeParameter)));
124            }
125    
126            return result;
127        }
128    
129        private static Collection<PsiTypeParameter> getReversedTypeParameters(@NotNull JavaClassImpl classifier) {
130            Iterable<PsiTypeParameter> parameters = PsiUtil.typeParametersIterable(classifier.getPsi());
131            List<PsiTypeParameter> result = new ArrayList<PsiTypeParameter>();
132    
133            for (PsiTypeParameter parameter : parameters) {
134                result.add(parameter);
135            }
136    
137            Collections.reverse(result);
138    
139            return result;
140        }
141    }