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