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 kotlin.Function1;
020    import kotlin.Unit;
021    import org.jetbrains.annotations.NotNull;
022    import org.jetbrains.annotations.Nullable;
023    import org.jetbrains.jet.lang.descriptors.*;
024    import org.jetbrains.jet.lang.descriptors.impl.TypeParameterDescriptorImpl;
025    import org.jetbrains.jet.lang.resolve.OverridingUtil;
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.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    
034    import java.util.*;
035    
036    public final class DescriptorResolverUtils {
037        private DescriptorResolverUtils() {
038        }
039    
040        @NotNull
041        public static <D extends CallableMemberDescriptor> Collection<D> resolveOverrides(
042                @NotNull Name name,
043                @NotNull Collection<D> membersFromSupertypes,
044                @NotNull Collection<D> membersFromCurrent,
045                @NotNull ClassDescriptor classDescriptor,
046                @NotNull final ErrorReporter errorReporter
047        ) {
048            final Set<D> result = new HashSet<D>();
049    
050            OverridingUtil.generateOverridesInFunctionGroup(
051                    name, membersFromSupertypes, membersFromCurrent, classDescriptor,
052                    new OverridingUtil.DescriptorSink() {
053                        @Override
054                        @SuppressWarnings("unchecked")
055                        public void addToScope(@NotNull CallableMemberDescriptor fakeOverride) {
056                            OverridingUtil.resolveUnknownVisibilityForMember(fakeOverride, new Function1<CallableMemberDescriptor, Unit>() {
057                                @Override
058                                public Unit invoke(@NotNull CallableMemberDescriptor descriptor) {
059                                    errorReporter.reportCannotInferVisibility(descriptor);
060                                    return Unit.INSTANCE$;
061                                }
062                            });
063                            result.add((D) fakeOverride);
064                        }
065    
066                        @Override
067                        public void conflict(@NotNull CallableMemberDescriptor fromSuper, @NotNull CallableMemberDescriptor fromCurrent) {
068                            // nop
069                        }
070                    }
071            );
072    
073            return result;
074        }
075    
076        @Nullable
077        public static ValueParameterDescriptor getAnnotationParameterByName(@NotNull Name name, @NotNull ClassDescriptor annotationClass) {
078            Collection<ConstructorDescriptor> constructors = annotationClass.getConstructors();
079            assert constructors.size() == 1 : "Annotation class descriptor must have only one constructor";
080    
081            for (ValueParameterDescriptor parameter : constructors.iterator().next().getValueParameters()) {
082                if (parameter.getName().equals(name)) {
083                    return parameter;
084                }
085            }
086    
087            return null;
088        }
089    
090        public static boolean isObjectMethodInInterface(@NotNull JavaMember member) {
091            return member.getContainingClass().isInterface() && member instanceof JavaMethod && isObjectMethod((JavaMethod) member);
092        }
093    
094        public static boolean isObjectMethod(@NotNull JavaMethod method) {
095            String name = method.getName().asString();
096            if (name.equals("toString") || name.equals("hashCode")) {
097                return method.getValueParameters().isEmpty();
098            }
099            else if (name.equals("equals")) {
100                return isMethodWithOneParameterWithFqName(method, "java.lang.Object");
101            }
102            return false;
103        }
104    
105        private static boolean isMethodWithOneParameterWithFqName(@NotNull JavaMethod method, @NotNull String fqName) {
106            List<JavaValueParameter> parameters = method.getValueParameters();
107            if (parameters.size() == 1) {
108                JavaType type = parameters.get(0).getType();
109                if (type instanceof JavaClassifierType) {
110                    JavaClassifier classifier = ((JavaClassifierType) type).getClassifier();
111                    if (classifier instanceof JavaClass) {
112                        FqName classFqName = ((JavaClass) classifier).getFqName();
113                        return classFqName != null && classFqName.asString().equals(fqName);
114                    }
115                }
116            }
117            return false;
118        }
119    
120        @NotNull
121        public static Collection<JavaClass> getClassesInPackage(@NotNull JavaPackage javaPackage) {
122            Collection<JavaClass> classes = javaPackage.getClasses();
123            Set<FqName> addedQualifiedNames = new HashSet<FqName>(classes.size());
124            List<JavaClass> result = new ArrayList<JavaClass>(classes.size());
125    
126            for (JavaClass javaClass : classes) {
127                FqName fqName = javaClass.getFqName();
128                if (fqName != null && addedQualifiedNames.add(fqName)) {
129                    result.add(javaClass);
130                }
131            }
132    
133            return result;
134        }
135    
136        /**
137         * @see com.intellij.psi.util.TypeConversionUtil#erasure(com.intellij.psi.PsiType)
138         */
139        @Nullable
140        public static JavaType erasure(@NotNull JavaType type) {
141            return erasure(type, JavaTypeSubstitutor.EMPTY);
142        }
143    
144        /**
145         * @see com.intellij.psi.util.TypeConversionUtil#erasure(com.intellij.psi.PsiType, com.intellij.psi.PsiSubstitutor)
146         */
147        @Nullable
148        public static JavaType erasure(@NotNull JavaType type, @NotNull JavaTypeSubstitutor substitutor) {
149            if (type instanceof JavaClassifierType) {
150                JavaClassifier classifier = ((JavaClassifierType) type).getClassifier();
151                if (classifier instanceof JavaClass) {
152                    return ((JavaClass) classifier).getDefaultType();
153                }
154                else if (classifier instanceof JavaTypeParameter) {
155                    JavaTypeParameter typeParameter = (JavaTypeParameter) classifier;
156                    return typeParameterErasure(typeParameter, new HashSet<JavaTypeParameter>(), substitutor);
157                }
158                else {
159                    return null;
160                }
161            }
162            else if (type instanceof JavaPrimitiveType) {
163                return type;
164            }
165            else if (type instanceof JavaArrayType) {
166                JavaType erasure = erasure(((JavaArrayType) type).getComponentType(), substitutor);
167                return erasure == null ? null : erasure.createArrayType();
168            }
169            else if (type instanceof JavaWildcardType) {
170                JavaWildcardType wildcardType = (JavaWildcardType) type;
171                JavaType bound = wildcardType.getBound();
172                if (bound != null && wildcardType.isExtends()) {
173                    return erasure(bound, substitutor);
174                }
175                return wildcardType.getTypeProvider().createJavaLangObjectType();
176            }
177            else {
178                throw new IllegalStateException("Unsupported type: " + type);
179            }
180        }
181    
182        /**
183         * @see com.intellij.psi.util.TypeConversionUtil#typeParameterErasure(com.intellij.psi.PsiTypeParameter)
184         */
185        @Nullable
186        private static JavaType typeParameterErasure(
187                @NotNull JavaTypeParameter typeParameter,
188                @NotNull HashSet<JavaTypeParameter> visited,
189                @NotNull JavaTypeSubstitutor substitutor
190        ) {
191            Collection<JavaClassifierType> upperBounds = typeParameter.getUpperBounds();
192            if (!upperBounds.isEmpty()) {
193                JavaClassifier classifier = upperBounds.iterator().next().getClassifier();
194                if (classifier instanceof JavaTypeParameter && !visited.contains(classifier)) {
195                    JavaTypeParameter typeParameterBound = (JavaTypeParameter) classifier;
196                    visited.add(typeParameterBound);
197                    JavaType substitutedType = substitutor.substitute(typeParameterBound);
198                    if (substitutedType != null) {
199                        return erasure(substitutedType);
200                    }
201                    return typeParameterErasure(typeParameterBound, visited, substitutor);
202                }
203                else if (classifier instanceof JavaClass) {
204                    return ((JavaClass) classifier).getDefaultType();
205                }
206            }
207            return typeParameter.getTypeProvider().createJavaLangObjectType();
208        }
209    
210        @NotNull
211        public static Map<TypeParameterDescriptor, TypeParameterDescriptorImpl> recreateTypeParametersAndReturnMapping(
212                @NotNull List<TypeParameterDescriptor> originalParameters,
213                @Nullable DeclarationDescriptor newOwner
214        ) {
215            // LinkedHashMap to save the order of type parameters
216            Map<TypeParameterDescriptor, TypeParameterDescriptorImpl> result =
217                    new LinkedHashMap<TypeParameterDescriptor, TypeParameterDescriptorImpl>();
218            for (TypeParameterDescriptor typeParameter : originalParameters) {
219                result.put(typeParameter,
220                           TypeParameterDescriptorImpl.createForFurtherModification(
221                                   newOwner == null ? typeParameter.getContainingDeclaration() : newOwner,
222                                   typeParameter.getAnnotations(),
223                                   typeParameter.isReified(),
224                                   typeParameter.getVariance(),
225                                   typeParameter.getName(),
226                                   typeParameter.getIndex(),
227                                   SourceElement.NO_SOURCE)
228                );
229            }
230            return result;
231        }
232    
233        @NotNull
234        public static TypeSubstitutor createSubstitutorForTypeParameters(
235                @NotNull Map<TypeParameterDescriptor, TypeParameterDescriptorImpl> originalToAltTypeParameters
236        ) {
237            Map<TypeConstructor, TypeProjection> typeSubstitutionContext = new HashMap<TypeConstructor, TypeProjection>();
238            for (Map.Entry<TypeParameterDescriptor, TypeParameterDescriptorImpl> originalToAltTypeParameter : originalToAltTypeParameters
239                    .entrySet()) {
240                typeSubstitutionContext.put(originalToAltTypeParameter.getKey().getTypeConstructor(),
241                                            new TypeProjectionImpl(originalToAltTypeParameter.getValue().getDefaultType()));
242            }
243            return TypeSubstitutor.create(typeSubstitutionContext);
244        }
245    }