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 com.intellij.psi.PsiClass;
020    import org.jetbrains.annotations.NotNull;
021    import org.jetbrains.annotations.Nullable;
022    import org.jetbrains.jet.lang.descriptors.ClassDescriptor;
023    import org.jetbrains.jet.lang.descriptors.ClassKind;
024    import org.jetbrains.jet.lang.descriptors.Modality;
025    import org.jetbrains.jet.lang.descriptors.TypeParameterDescriptor;
026    import org.jetbrains.jet.lang.resolve.BindingTrace;
027    import org.jetbrains.jet.lang.resolve.java.DescriptorResolverUtils;
028    import org.jetbrains.jet.lang.resolve.java.JavaSemanticServices;
029    import org.jetbrains.jet.lang.resolve.java.descriptor.ClassDescriptorFromJvmBytecode;
030    import org.jetbrains.jet.lang.resolve.java.kt.JetClassObjectAnnotation;
031    import org.jetbrains.jet.lang.resolve.java.provider.ClassPsiDeclarationProvider;
032    import org.jetbrains.jet.lang.resolve.java.scope.JavaClassNonStaticMembersScope;
033    import org.jetbrains.jet.lang.resolve.java.wrapper.PsiClassWrapper;
034    import org.jetbrains.jet.lang.resolve.name.FqName;
035    import org.jetbrains.jet.lang.resolve.name.FqNameBase;
036    import org.jetbrains.jet.lang.resolve.name.FqNameUnsafe;
037    import org.jetbrains.jet.lang.resolve.name.Name;
038    import org.jetbrains.jet.lang.resolve.scopes.RedeclarationHandler;
039    import org.jetbrains.jet.lang.resolve.scopes.WritableScope;
040    import org.jetbrains.jet.lang.resolve.scopes.WritableScopeImpl;
041    
042    import javax.inject.Inject;
043    import java.util.Collections;
044    
045    import static org.jetbrains.jet.lang.resolve.DescriptorResolver.createEnumClassObjectValueOfMethod;
046    import static org.jetbrains.jet.lang.resolve.DescriptorResolver.createEnumClassObjectValuesMethod;
047    import static org.jetbrains.jet.lang.resolve.DescriptorUtils.getClassObjectName;
048    
049    public 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    }