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 }