001    /*
002     * Copyright 2010-2013 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.jet.lang.resolve.java.resolver;
018    
019    import com.google.common.collect.Maps;
020    import org.jetbrains.annotations.NotNull;
021    import org.jetbrains.annotations.Nullable;
022    import org.jetbrains.jet.lang.descriptors.*;
023    import org.jetbrains.jet.lang.descriptors.impl.TypeParameterDescriptorImpl;
024    import org.jetbrains.jet.lang.resolve.OverrideResolver;
025    import org.jetbrains.jet.lang.resolve.java.JvmAnnotationNames;
026    import org.jetbrains.jet.lang.resolve.java.PackageClassUtils;
027    import org.jetbrains.jet.lang.resolve.java.structure.*;
028    import org.jetbrains.jet.lang.resolve.name.FqName;
029    import org.jetbrains.jet.lang.resolve.name.Name;
030    import org.jetbrains.jet.lang.types.TypeConstructor;
031    import org.jetbrains.jet.lang.types.TypeProjection;
032    import org.jetbrains.jet.lang.types.TypeSubstitutor;
033    
034    import java.util.*;
035    
036    import static org.jetbrains.jet.lang.resolve.DescriptorUtils.isEnumClassObject;
037    
038    public final class DescriptorResolverUtils {
039        private DescriptorResolverUtils() {
040        }
041    
042        public static boolean isCompiledKotlinPackageClass(@NotNull JavaClass javaClass) {
043            if (javaClass.getOriginKind() == JavaClass.OriginKind.COMPILED) {
044                FqName fqName = javaClass.getFqName();
045                if (fqName != null && PackageClassUtils.isPackageClassFqName(fqName)) {
046                    return javaClass.findAnnotation(JvmAnnotationNames.KOTLIN_PACKAGE.getFqName()) != null;
047                }
048            }
049            return false;
050        }
051    
052        public static boolean isCompiledKotlinClass(@NotNull JavaClass javaClass) {
053            if (javaClass.getOriginKind() == JavaClass.OriginKind.COMPILED) {
054                return javaClass.findAnnotation(JvmAnnotationNames.KOTLIN_CLASS.getFqName()) != null;
055            }
056            return false;
057        }
058    
059        public static boolean isCompiledKotlinClassOrPackageClass(@NotNull JavaClass javaClass) {
060            return isCompiledKotlinClass(javaClass) || isCompiledKotlinPackageClass(javaClass);
061        }
062    
063        @NotNull
064        public static <D extends CallableMemberDescriptor> Collection<D> resolveOverrides(
065                @NotNull Name name,
066                @NotNull Collection<D> membersFromSupertypes,
067                @NotNull Collection<D> membersFromCurrent,
068                @NotNull ClassDescriptor classDescriptor,
069                @NotNull final FakeOverrideVisibilityResolver visibilityResolver
070        ) {
071            final Set<D> result = new HashSet<D>();
072    
073            OverrideResolver.generateOverridesInFunctionGroup(
074                    name, membersFromSupertypes, membersFromCurrent, classDescriptor,
075                    new OverrideResolver.DescriptorSink() {
076                        @Override
077                        @SuppressWarnings("unchecked")
078                        public void addToScope(@NotNull CallableMemberDescriptor fakeOverride) {
079                            visibilityResolver.resolveUnknownVisibilityForMember(fakeOverride);
080                            result.add((D) fakeOverride);
081                        }
082    
083                        @Override
084                        public void conflict(@NotNull CallableMemberDescriptor fromSuper, @NotNull CallableMemberDescriptor fromCurrent) {
085                            // nop
086                        }
087                    }
088            );
089    
090            return result;
091        }
092    
093        @Nullable
094        public static ValueParameterDescriptor getAnnotationParameterByName(@NotNull Name name, @NotNull ClassDescriptor annotationClass) {
095            Collection<ConstructorDescriptor> constructors = annotationClass.getConstructors();
096            assert constructors.size() == 1 : "Annotation class descriptor must have only one constructor";
097    
098            for (ValueParameterDescriptor parameter : constructors.iterator().next().getValueParameters()) {
099                if (parameter.getName().equals(name)) {
100                    return parameter;
101                }
102            }
103    
104            return null;
105        }
106    
107        /**
108         * @return true if {@code member} is a static member of enum class, which is to be put into its class object (and not into the
109         *         corresponding package). This applies to enum entries, values() and valueOf(String) methods
110         */
111        public static boolean shouldBeInEnumClassObject(@NotNull JavaMember member) {
112            if (!member.getContainingClass().isEnum()) return false;
113    
114            if (member instanceof JavaField && ((JavaField) member).isEnumEntry()) return true;
115    
116            if (!(member instanceof JavaMethod)) return false;
117            String signature = JavaSignatureFormatter.getInstance().formatMethod((JavaMethod) member);
118    
119            return "values()".equals(signature) ||
120                   "valueOf(java.lang.String)".equals(signature);
121        }
122    
123        public static boolean isCorrectOwnerForEnumMember(@NotNull ClassOrNamespaceDescriptor ownerDescriptor, @NotNull JavaMember member) {
124            return isEnumClassObject(ownerDescriptor) == shouldBeInEnumClassObject(member);
125        }
126    
127        public static boolean isObjectMethodInInterface(@NotNull JavaMember member) {
128            return member.getContainingClass().isInterface() && member instanceof JavaMethod && isObjectMethod((JavaMethod) member);
129        }
130    
131        public static boolean isObjectMethod(@NotNull JavaMethod method) {
132            String signature = JavaSignatureFormatter.getInstance().formatMethod(method);
133            return "hashCode()".equals(signature) ||
134                   "equals(java.lang.Object)".equals(signature) ||
135                   "toString()".equals(signature);
136        }
137    
138        @NotNull
139        public static Collection<JavaClass> getClassesInPackage(@NotNull JavaPackage javaPackage) {
140            Collection<JavaClass> classes = javaPackage.getClasses();
141            Set<FqName> addedQualifiedNames = new HashSet<FqName>(classes.size());
142            List<JavaClass> result = new ArrayList<JavaClass>(classes.size());
143    
144            for (JavaClass javaClass : classes) {
145                FqName fqName = javaClass.getFqName();
146                if (fqName != null && addedQualifiedNames.add(fqName)) {
147                    result.add(javaClass);
148                }
149            }
150    
151            return result;
152        }
153    
154        /**
155         * @see com.intellij.psi.util.TypeConversionUtil#erasure(com.intellij.psi.PsiType)
156         */
157        @Nullable
158        public static JavaType erasure(@NotNull JavaType type) {
159            return erasure(type, JavaTypeSubstitutor.EMPTY);
160        }
161    
162        /**
163         * @see com.intellij.psi.util.TypeConversionUtil#erasure(com.intellij.psi.PsiType, com.intellij.psi.PsiSubstitutor)
164         */
165        @Nullable
166        public static JavaType erasure(@NotNull JavaType type, @NotNull JavaTypeSubstitutor substitutor) {
167            if (type instanceof JavaClassifierType) {
168                JavaClassifier classifier = ((JavaClassifierType) type).getClassifier();
169                if (classifier instanceof JavaClass) {
170                    return ((JavaClass) classifier).getDefaultType();
171                }
172                else if (classifier instanceof JavaTypeParameter) {
173                    JavaTypeParameter typeParameter = (JavaTypeParameter) classifier;
174                    return typeParameterErasure(typeParameter, new HashSet<JavaTypeParameter>(), substitutor);
175                }
176                else {
177                    return null;
178                }
179            }
180            else if (type instanceof JavaPrimitiveType) {
181                return type;
182            }
183            else if (type instanceof JavaArrayType) {
184                JavaType erasure = erasure(((JavaArrayType) type).getComponentType(), substitutor);
185                return erasure == null ? null : JavaElementFactory.getInstance().createArrayType(erasure);
186            }
187            else if (type instanceof JavaWildcardType) {
188                JavaWildcardType wildcardType = (JavaWildcardType) type;
189                JavaType bound = wildcardType.getBound();
190                if (bound != null && wildcardType.isExtends()) {
191                    return erasure(bound, substitutor);
192                }
193                return wildcardType.getTypeProvider().createJavaLangObjectType();
194            }
195            else {
196                throw new IllegalStateException("Unsupported type: " + type);
197            }
198        }
199    
200        /**
201         * @see com.intellij.psi.util.TypeConversionUtil#typeParameterErasure(com.intellij.psi.PsiTypeParameter)
202         */
203        @Nullable
204        private static JavaType typeParameterErasure(
205                @NotNull JavaTypeParameter typeParameter,
206                @NotNull HashSet<JavaTypeParameter> visited,
207                @NotNull JavaTypeSubstitutor substitutor
208        ) {
209            Collection<JavaClassifierType> upperBounds = typeParameter.getUpperBounds();
210            if (!upperBounds.isEmpty()) {
211                JavaClassifier classifier = upperBounds.iterator().next().getClassifier();
212                if (classifier instanceof JavaTypeParameter && !visited.contains(classifier)) {
213                    JavaTypeParameter typeParameterBound = (JavaTypeParameter) classifier;
214                    visited.add(typeParameterBound);
215                    JavaType substitutedType = substitutor.substitute(typeParameterBound);
216                    if (substitutedType != null) {
217                        return erasure(substitutedType);
218                    }
219                    return typeParameterErasure(typeParameterBound, visited, substitutor);
220                }
221                else if (classifier instanceof JavaClass) {
222                    return ((JavaClass) classifier).getDefaultType();
223                }
224            }
225            return typeParameter.getTypeProvider().createJavaLangObjectType();
226        }
227    
228        @NotNull
229        public static Map<TypeParameterDescriptor, TypeParameterDescriptorImpl> recreateTypeParametersAndReturnMapping(
230                @NotNull List<TypeParameterDescriptor> originalParameters,
231                @Nullable DeclarationDescriptor newOwner
232        ) {
233            Map<TypeParameterDescriptor, TypeParameterDescriptorImpl> result = Maps.newLinkedHashMap(); // save order of type parameters
234            for (TypeParameterDescriptor typeParameter : originalParameters) {
235                result.put(typeParameter,
236                           TypeParameterDescriptorImpl.createForFurtherModification(
237                                   newOwner == null ? typeParameter.getContainingDeclaration() : newOwner,
238                                   typeParameter.getAnnotations(),
239                                   typeParameter.isReified(),
240                                   typeParameter.getVariance(),
241                                   typeParameter.getName(),
242                                   typeParameter.getIndex()));
243            }
244            return result;
245        }
246    
247        @NotNull
248        public static TypeSubstitutor createSubstitutorForTypeParameters(
249                @NotNull Map<TypeParameterDescriptor, TypeParameterDescriptorImpl> originalToAltTypeParameters
250        ) {
251            Map<TypeConstructor, TypeProjection> typeSubstitutionContext = Maps.newHashMap();
252            for (Map.Entry<TypeParameterDescriptor, TypeParameterDescriptorImpl> originalToAltTypeParameter : originalToAltTypeParameters
253                    .entrySet()) {
254                typeSubstitutionContext.put(originalToAltTypeParameter.getKey().getTypeConstructor(),
255                                            new TypeProjection(originalToAltTypeParameter.getValue().getDefaultType()));
256            }
257            return TypeSubstitutor.create(typeSubstitutionContext);
258        }
259    }