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 }