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
017package org.jetbrains.jet.lang.resolve.java.resolver;
018
019import com.intellij.psi.PsiClass;
020import org.jetbrains.annotations.NotNull;
021import org.jetbrains.annotations.Nullable;
022import org.jetbrains.jet.lang.descriptors.ClassDescriptor;
023import org.jetbrains.jet.lang.descriptors.ClassKind;
024import org.jetbrains.jet.lang.descriptors.Modality;
025import org.jetbrains.jet.lang.descriptors.TypeParameterDescriptor;
026import org.jetbrains.jet.lang.resolve.BindingTrace;
027import org.jetbrains.jet.lang.resolve.java.DescriptorResolverUtils;
028import org.jetbrains.jet.lang.resolve.java.JavaSemanticServices;
029import org.jetbrains.jet.lang.resolve.java.descriptor.ClassDescriptorFromJvmBytecode;
030import org.jetbrains.jet.lang.resolve.java.kt.JetClassObjectAnnotation;
031import org.jetbrains.jet.lang.resolve.java.provider.ClassPsiDeclarationProvider;
032import org.jetbrains.jet.lang.resolve.java.scope.JavaClassNonStaticMembersScope;
033import org.jetbrains.jet.lang.resolve.java.wrapper.PsiClassWrapper;
034import org.jetbrains.jet.lang.resolve.name.FqName;
035import org.jetbrains.jet.lang.resolve.name.FqNameBase;
036import org.jetbrains.jet.lang.resolve.name.FqNameUnsafe;
037import org.jetbrains.jet.lang.resolve.name.Name;
038import org.jetbrains.jet.lang.resolve.scopes.RedeclarationHandler;
039import org.jetbrains.jet.lang.resolve.scopes.WritableScope;
040import org.jetbrains.jet.lang.resolve.scopes.WritableScopeImpl;
041
042import javax.inject.Inject;
043import java.util.Collections;
044
045import static org.jetbrains.jet.lang.resolve.DescriptorResolver.createEnumClassObjectValueOfMethod;
046import static org.jetbrains.jet.lang.resolve.DescriptorResolver.createEnumClassObjectValuesMethod;
047import static org.jetbrains.jet.lang.resolve.DescriptorUtils.getClassObjectName;
048
049public final class JavaClassObjectResolver {
050
051    private BindingTrace trace;
052    private JavaSemanticServices semanticServices;
053    private JavaSupertypeResolver supertypesResolver;
054
055    @Inject
056    public void setSupertypesResolver(JavaSupertypeResolver supertypesResolver) {
057        this.supertypesResolver = supertypesResolver;
058    }
059
060    @Inject
061    public void setTrace(BindingTrace trace) {
062        this.trace = trace;
063    }
064
065    @Inject
066    public void setSemanticServices(JavaSemanticServices semanticServices) {
067        this.semanticServices = semanticServices;
068    }
069
070    @Nullable
071    public ClassDescriptorFromJvmBytecode createClassObjectDescriptor(
072            @NotNull ClassDescriptor containing,
073            @NotNull PsiClass psiClass
074    ) {
075        DescriptorResolverUtils.checkPsiClassIsNotJet(psiClass);
076
077        if (psiClass.isEnum()) {
078            return createClassObjectDescriptorForEnum(containing, psiClass);
079        }
080
081        if (!DescriptorResolverUtils.isKotlinClass(psiClass)) {
082            return null;
083        }
084
085        PsiClass classObjectPsiClass = getClassObjectPsiClass(psiClass);
086        if (classObjectPsiClass == null) {
087            return null;
088        }
089
090        return createClassObjectFromPsi(containing, classObjectPsiClass);
091    }
092
093    @NotNull
094    private ClassDescriptorFromJvmBytecode createClassObjectFromPsi(
095            @NotNull ClassDescriptor containing,
096            @NotNull PsiClass classObjectPsiClass
097    ) {
098        String qualifiedName = classObjectPsiClass.getQualifiedName();
099        assert qualifiedName != null;
100        FqName fqName = new FqName(qualifiedName);
101        ClassPsiDeclarationProvider classObjectData = semanticServices.getPsiDeclarationProviderFactory().createBinaryClassData(classObjectPsiClass);
102        ClassDescriptorFromJvmBytecode classObjectDescriptor
103                = new ClassDescriptorFromJvmBytecode(containing, ClassKind.CLASS_OBJECT, false);
104        classObjectDescriptor.setSupertypes(supertypesResolver.getSupertypes(classObjectDescriptor,
105                                                                             new PsiClassWrapper(classObjectPsiClass),
106                                                                             classObjectData,
107                                                                             Collections.<TypeParameterDescriptor>emptyList()));
108        setUpClassObjectDescriptor(classObjectDescriptor, containing, fqName, classObjectData, getClassObjectName(containing.getName()));
109        return classObjectDescriptor;
110    }
111
112    @NotNull
113    private ClassDescriptorFromJvmBytecode createClassObjectDescriptorForEnum(
114            @NotNull ClassDescriptor containing,
115            @NotNull PsiClass psiClass
116    ) {
117        ClassDescriptorFromJvmBytecode classObjectDescriptor = createSyntheticClassObject(containing, psiClass);
118
119        classObjectDescriptor.getBuilder().addFunctionDescriptor(createEnumClassObjectValuesMethod(classObjectDescriptor, trace));
120        classObjectDescriptor.getBuilder().addFunctionDescriptor(createEnumClassObjectValueOfMethod(classObjectDescriptor, trace));
121
122        return classObjectDescriptor;
123    }
124
125    @NotNull
126    private ClassDescriptorFromJvmBytecode createSyntheticClassObject(
127            @NotNull ClassDescriptor containing,
128            @NotNull PsiClass psiClass
129    ) {
130        FqNameUnsafe fqName = DescriptorResolverUtils.getFqNameForClassObject(psiClass);
131        ClassDescriptorFromJvmBytecode classObjectDescriptor =
132                new ClassDescriptorFromJvmBytecode(containing, ClassKind.CLASS_OBJECT, false);
133        ClassPsiDeclarationProvider data = semanticServices.getPsiDeclarationProviderFactory().createSyntheticClassObjectClassData(psiClass);
134        setUpClassObjectDescriptor(classObjectDescriptor, containing, fqName, data, getClassObjectName(containing.getName().asString()));
135        return classObjectDescriptor;
136    }
137
138    private void setUpClassObjectDescriptor(
139            @NotNull ClassDescriptorFromJvmBytecode classObjectDescriptor,
140            @NotNull ClassDescriptor containing,
141            @NotNull FqNameBase fqName,
142            @NotNull ClassPsiDeclarationProvider data,
143            @NotNull Name classObjectName
144    ) {
145        classObjectDescriptor.setName(classObjectName);
146        classObjectDescriptor.setModality(Modality.FINAL);
147        classObjectDescriptor.setVisibility(containing.getVisibility());
148        classObjectDescriptor.setTypeParameterDescriptors(Collections.<TypeParameterDescriptor>emptyList());
149        classObjectDescriptor.createTypeConstructor();
150        JavaClassNonStaticMembersScope classMembersScope = new JavaClassNonStaticMembersScope(classObjectDescriptor, data, semanticServices);
151        WritableScopeImpl writableScope =
152                new WritableScopeImpl(classMembersScope, classObjectDescriptor, RedeclarationHandler.THROW_EXCEPTION, "Member lookup scope");
153        writableScope.changeLockLevel(WritableScope.LockLevel.BOTH);
154        classObjectDescriptor.setScopeForMemberLookup(writableScope);
155        classObjectDescriptor.setScopeForConstructorResolve(classMembersScope);
156    }
157
158
159    @Nullable
160    private static PsiClass getClassObjectPsiClass(@NotNull PsiClass ownerClass) {
161        for (PsiClass inner : ownerClass.getInnerClasses()) {
162            if (JetClassObjectAnnotation.get(inner).isDefined()) {
163                return inner;
164            }
165        }
166        return null;
167    }
168}