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 org.jetbrains.annotations.NotNull;
020    import org.jetbrains.annotations.Nullable;
021    import org.jetbrains.jet.lang.descriptors.*;
022    import org.jetbrains.jet.lang.descriptors.impl.TypeParameterDescriptorImpl;
023    import org.jetbrains.jet.lang.resolve.OverridingUtil;
024    import org.jetbrains.jet.lang.resolve.java.JvmAnnotationNames;
025    import org.jetbrains.jet.lang.resolve.java.sam.SingleAbstractMethodUtils;
026    import org.jetbrains.jet.lang.resolve.java.structure.*;
027    import org.jetbrains.jet.lang.resolve.name.FqName;
028    import org.jetbrains.jet.lang.resolve.name.Name;
029    import org.jetbrains.jet.lang.resolve.scopes.JetScope;
030    import org.jetbrains.jet.lang.types.TypeConstructor;
031    import org.jetbrains.jet.lang.types.TypeProjection;
032    import org.jetbrains.jet.lang.types.TypeProjectionImpl;
033    import org.jetbrains.jet.lang.types.TypeSubstitutor;
034    import org.jetbrains.jet.lang.types.lang.KotlinBuiltIns;
035    
036    import java.util.*;
037    
038    public final class DescriptorResolverUtils {
039        public static final FqName OBJECT_FQ_NAME = new FqName("java.lang.Object");
040    
041        private DescriptorResolverUtils() {
042        }
043    
044        public static boolean isCompiledKotlinPackageClass(@NotNull JavaClass javaClass) {
045            if (javaClass.getOriginKind() == JavaClass.OriginKind.COMPILED) {
046                return javaClass.findAnnotation(JvmAnnotationNames.KOTLIN_PACKAGE) != null
047                       || javaClass.findAnnotation(JvmAnnotationNames.KOTLIN_SYNTHETIC_CLASS) != null;
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) != null;
055            }
056            return false;
057        }
058    
059        private static boolean isCompiledKotlinClassOrPackageClass(@NotNull JavaClass javaClass) {
060            return isCompiledKotlinClass(javaClass) || isCompiledKotlinPackageClass(javaClass);
061        }
062    
063        @NotNull
064        public static FqName fqNameByClass(@NotNull Class<?> clazz) {
065            return new FqName(clazz.getCanonicalName());
066        }
067    
068        @NotNull
069        public static <D extends CallableMemberDescriptor> Collection<D> resolveOverrides(
070                @NotNull Name name,
071                @NotNull Collection<D> membersFromSupertypes,
072                @NotNull Collection<D> membersFromCurrent,
073                @NotNull ClassDescriptor classDescriptor,
074                @NotNull final ErrorReporter errorReporter
075        ) {
076            final Set<D> result = new HashSet<D>();
077    
078            OverridingUtil.generateOverridesInFunctionGroup(
079                    name, membersFromSupertypes, membersFromCurrent, classDescriptor,
080                    new OverridingUtil.DescriptorSink() {
081                        @Override
082                        @SuppressWarnings("unchecked")
083                        public void addToScope(@NotNull CallableMemberDescriptor fakeOverride) {
084                            OverridingUtil.resolveUnknownVisibilityForMember(fakeOverride, new OverridingUtil.NotInferredVisibilitySink() {
085                                @Override
086                                public void cannotInferVisibility(@NotNull CallableMemberDescriptor descriptor) {
087                                    errorReporter.reportCannotInferVisibility(descriptor);
088                                }
089                            });
090                            result.add((D) fakeOverride);
091                        }
092    
093                        @Override
094                        public void conflict(@NotNull CallableMemberDescriptor fromSuper, @NotNull CallableMemberDescriptor fromCurrent) {
095                            // nop
096                        }
097                    }
098            );
099    
100            return result;
101        }
102    
103        @Nullable
104        public static ValueParameterDescriptor getAnnotationParameterByName(@NotNull Name name, @NotNull ClassDescriptor annotationClass) {
105            Collection<ConstructorDescriptor> constructors = annotationClass.getConstructors();
106            assert constructors.size() == 1 : "Annotation class descriptor must have only one constructor";
107    
108            for (ValueParameterDescriptor parameter : constructors.iterator().next().getValueParameters()) {
109                if (parameter.getName().equals(name)) {
110                    return parameter;
111                }
112            }
113    
114            return null;
115        }
116    
117        /**
118         * @return true if {@code method} is a static method of enum class, which is to be put into its class object (and not into the
119         *         corresponding package). This applies to values() and valueOf(String) methods
120         */
121        public static boolean shouldBeInEnumClassObject(@NotNull JavaMethod method) {
122            if (!method.getContainingClass().isEnum()) return false;
123    
124            String signature = JavaSignatureFormatter.getInstance().formatMethod(method);
125    
126            return "values()".equals(signature) ||
127                   "valueOf(java.lang.String)".equals(signature);
128        }
129    
130        public static boolean isObjectMethodInInterface(@NotNull JavaMember member) {
131            return member.getContainingClass().isInterface() && member instanceof JavaMethod && isObjectMethod((JavaMethod) member);
132        }
133    
134        public static boolean isObjectMethod(@NotNull JavaMethod method) {
135            String signature = JavaSignatureFormatter.getInstance().formatMethod(method);
136            return "hashCode()".equals(signature) ||
137                   "equals(java.lang.Object)".equals(signature) ||
138                   "toString()".equals(signature);
139        }
140    
141        @NotNull
142        public static Collection<JavaClass> getClassesInPackage(@NotNull JavaPackage javaPackage) {
143            Collection<JavaClass> classes = javaPackage.getClasses();
144            Set<FqName> addedQualifiedNames = new HashSet<FqName>(classes.size());
145            List<JavaClass> result = new ArrayList<JavaClass>(classes.size());
146    
147            for (JavaClass javaClass : classes) {
148                FqName fqName = javaClass.getFqName();
149                if (fqName != null && addedQualifiedNames.add(fqName)) {
150                    result.add(javaClass);
151                }
152            }
153    
154            return result;
155        }
156    
157        /**
158         * @see com.intellij.psi.util.TypeConversionUtil#erasure(com.intellij.psi.PsiType)
159         */
160        @Nullable
161        public static JavaType erasure(@NotNull JavaType type) {
162            return erasure(type, JavaTypeSubstitutor.EMPTY);
163        }
164    
165        /**
166         * @see com.intellij.psi.util.TypeConversionUtil#erasure(com.intellij.psi.PsiType, com.intellij.psi.PsiSubstitutor)
167         */
168        @Nullable
169        public static JavaType erasure(@NotNull JavaType type, @NotNull JavaTypeSubstitutor substitutor) {
170            if (type instanceof JavaClassifierType) {
171                JavaClassifier classifier = ((JavaClassifierType) type).getClassifier();
172                if (classifier instanceof JavaClass) {
173                    return ((JavaClass) classifier).getDefaultType();
174                }
175                else if (classifier instanceof JavaTypeParameter) {
176                    JavaTypeParameter typeParameter = (JavaTypeParameter) classifier;
177                    return typeParameterErasure(typeParameter, new HashSet<JavaTypeParameter>(), substitutor);
178                }
179                else {
180                    return null;
181                }
182            }
183            else if (type instanceof JavaPrimitiveType) {
184                return type;
185            }
186            else if (type instanceof JavaArrayType) {
187                JavaType erasure = erasure(((JavaArrayType) type).getComponentType(), substitutor);
188                return erasure == null ? null : JavaElementFactory.getInstance().createArrayType(erasure);
189            }
190            else if (type instanceof JavaWildcardType) {
191                JavaWildcardType wildcardType = (JavaWildcardType) type;
192                JavaType bound = wildcardType.getBound();
193                if (bound != null && wildcardType.isExtends()) {
194                    return erasure(bound, substitutor);
195                }
196                return wildcardType.getTypeProvider().createJavaLangObjectType();
197            }
198            else {
199                throw new IllegalStateException("Unsupported type: " + type);
200            }
201        }
202    
203        /**
204         * @see com.intellij.psi.util.TypeConversionUtil#typeParameterErasure(com.intellij.psi.PsiTypeParameter)
205         */
206        @Nullable
207        private static JavaType typeParameterErasure(
208                @NotNull JavaTypeParameter typeParameter,
209                @NotNull HashSet<JavaTypeParameter> visited,
210                @NotNull JavaTypeSubstitutor substitutor
211        ) {
212            Collection<JavaClassifierType> upperBounds = typeParameter.getUpperBounds();
213            if (!upperBounds.isEmpty()) {
214                JavaClassifier classifier = upperBounds.iterator().next().getClassifier();
215                if (classifier instanceof JavaTypeParameter && !visited.contains(classifier)) {
216                    JavaTypeParameter typeParameterBound = (JavaTypeParameter) classifier;
217                    visited.add(typeParameterBound);
218                    JavaType substitutedType = substitutor.substitute(typeParameterBound);
219                    if (substitutedType != null) {
220                        return erasure(substitutedType);
221                    }
222                    return typeParameterErasure(typeParameterBound, visited, substitutor);
223                }
224                else if (classifier instanceof JavaClass) {
225                    return ((JavaClass) classifier).getDefaultType();
226                }
227            }
228            return typeParameter.getTypeProvider().createJavaLangObjectType();
229        }
230    
231        @NotNull
232        public static Map<TypeParameterDescriptor, TypeParameterDescriptorImpl> recreateTypeParametersAndReturnMapping(
233                @NotNull List<TypeParameterDescriptor> originalParameters,
234                @Nullable DeclarationDescriptor newOwner
235        ) {
236            // LinkedHashMap to save the order of type parameters
237            Map<TypeParameterDescriptor, TypeParameterDescriptorImpl> result =
238                    new LinkedHashMap<TypeParameterDescriptor, TypeParameterDescriptorImpl>();
239            for (TypeParameterDescriptor typeParameter : originalParameters) {
240                result.put(typeParameter,
241                           TypeParameterDescriptorImpl.createForFurtherModification(
242                                   newOwner == null ? typeParameter.getContainingDeclaration() : newOwner,
243                                   typeParameter.getAnnotations(),
244                                   typeParameter.isReified(),
245                                   typeParameter.getVariance(),
246                                   typeParameter.getName(),
247                                   typeParameter.getIndex()));
248            }
249            return result;
250        }
251    
252        @NotNull
253        public static TypeSubstitutor createSubstitutorForTypeParameters(
254                @NotNull Map<TypeParameterDescriptor, TypeParameterDescriptorImpl> originalToAltTypeParameters
255        ) {
256            Map<TypeConstructor, TypeProjection> typeSubstitutionContext = new HashMap<TypeConstructor, TypeProjection>();
257            for (Map.Entry<TypeParameterDescriptor, TypeParameterDescriptorImpl> originalToAltTypeParameter : originalToAltTypeParameters
258                    .entrySet()) {
259                typeSubstitutionContext.put(originalToAltTypeParameter.getKey().getTypeConstructor(),
260                                            new TypeProjectionImpl(originalToAltTypeParameter.getValue().getDefaultType()));
261            }
262            return TypeSubstitutor.create(typeSubstitutionContext);
263        }
264    
265        public static boolean isJavaClassVisibleAsPackage(@NotNull JavaClass javaClass) {
266            return !isCompiledKotlinClassOrPackageClass(javaClass) && hasStaticMembers(javaClass);
267        }
268    
269        private static boolean hasStaticMembers(@NotNull JavaClass javaClass) {
270            for (JavaMethod method : javaClass.getMethods()) {
271                if (method.isStatic() && !shouldBeInEnumClassObject(method)) {
272                    return true;
273                }
274            }
275    
276            for (JavaField field : javaClass.getFields()) {
277                if (field.isStatic() && !field.isEnumEntry()) {
278                    return true;
279                }
280            }
281    
282            for (JavaClass nestedClass : javaClass.getInnerClasses()) {
283                if (SingleAbstractMethodUtils.isSamInterface(nestedClass)) {
284                    return true;
285                }
286                if (nestedClass.isStatic() && hasStaticMembers(nestedClass)) {
287                    return true;
288                }
289            }
290    
291            return false;
292        }
293    
294        @Nullable
295        public static ClassDescriptor getKotlinBuiltinClassDescriptor(@NotNull FqName qualifiedName) {
296            if (!qualifiedName.firstSegmentIs(KotlinBuiltIns.BUILT_INS_PACKAGE_NAME)) return null;
297    
298            List<Name> segments = qualifiedName.pathSegments();
299            if (segments.size() < 2) return null;
300    
301            JetScope scope = KotlinBuiltIns.getInstance().getBuiltInsPackageScope();
302            for (int i = 1, size = segments.size(); i < size; i++) {
303                ClassifierDescriptor classifier = scope.getClassifier(segments.get(i));
304                if (classifier == null) return null;
305                assert classifier instanceof ClassDescriptor : "Unexpected classifier in built-ins: " + classifier;
306                scope = ((ClassDescriptor) classifier).getUnsubstitutedInnerClassesScope();
307            }
308    
309            return (ClassDescriptor) scope.getContainingDeclaration();
310        }
311    }