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