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.cli.jvm.compiler;
018    
019    import com.google.common.base.Predicate;
020    import com.google.common.collect.Collections2;
021    import com.intellij.openapi.application.ApplicationManager;
022    import com.intellij.openapi.components.ServiceManager;
023    import com.intellij.openapi.project.Project;
024    import com.intellij.psi.PsiClass;
025    import com.intellij.psi.PsiElement;
026    import com.intellij.psi.search.GlobalSearchScope;
027    import com.intellij.psi.search.PsiSearchScopeUtil;
028    import com.intellij.util.Function;
029    import com.intellij.util.SmartList;
030    import com.intellij.util.containers.ContainerUtil;
031    import org.jetbrains.annotations.NotNull;
032    import org.jetbrains.annotations.Nullable;
033    import org.jetbrains.jet.asJava.KotlinLightClassForExplicitDeclaration;
034    import org.jetbrains.jet.asJava.LightClassConstructionContext;
035    import org.jetbrains.jet.asJava.LightClassGenerationSupport;
036    import org.jetbrains.jet.codegen.binding.PsiCodegenPredictor;
037    import org.jetbrains.jet.lang.descriptors.ClassDescriptor;
038    import org.jetbrains.jet.lang.descriptors.DeclarationDescriptor;
039    import org.jetbrains.jet.lang.descriptors.NamespaceDescriptor;
040    import org.jetbrains.jet.lang.psi.*;
041    import org.jetbrains.jet.lang.resolve.*;
042    import org.jetbrains.jet.lang.resolve.java.JvmAbi;
043    import org.jetbrains.jet.lang.resolve.java.JvmClassName;
044    import org.jetbrains.jet.lang.resolve.name.FqName;
045    
046    import java.util.Collection;
047    import java.util.Collections;
048    import java.util.List;
049    
050    /**
051     * This class solves the problem of interdependency between analyzing Kotlin code and generating JetLightClasses
052     *
053     * Consider the following example:
054     *
055     * KClass.kt refers to JClass.java and vice versa
056     *
057     * To analyze KClass.kt we need to load descriptors from JClass.java, and to do that we need a JetLightClass instance for KClass,
058     * which can only be constructed when the structure of KClass is known.
059     *
060     * To mitigate this, CliLightClassGenerationSupport hold a trace that is shared between the analyzer and JetLightClasses
061     */
062    public class CliLightClassGenerationSupport extends LightClassGenerationSupport {
063    
064        public static CliLightClassGenerationSupport getInstanceForCli(@NotNull Project project) {
065            return ServiceManager.getService(project, CliLightClassGenerationSupport.class);
066        }
067    
068        private BindingTrace trace;
069    
070        public CliLightClassGenerationSupport() {
071        }
072    
073        @NotNull
074        public BindingTrace getTrace() {
075            if (trace == null) {
076                trace = new BindingTraceContext();
077            }
078            return trace;
079        }
080    
081        public void newBindingTrace() {
082            assert ApplicationManager.getApplication().isUnitTestMode() : "Mutating project service's state shouldn't happen other than in tests";
083            trace = null;
084        }
085    
086        @NotNull
087        @Override
088        public LightClassConstructionContext analyzeRelevantCode(@NotNull Collection<JetFile> files) {
089            return new LightClassConstructionContext(getTrace().getBindingContext(), null);
090        }
091    
092        @NotNull
093        @Override
094        public Collection<JetClassOrObject> findClassOrObjectDeclarations(@NotNull FqName fqName, @NotNull GlobalSearchScope searchScope) {
095            ClassDescriptor classDescriptor = getTrace().get(BindingContext.FQNAME_TO_CLASS_DESCRIPTOR, fqName);
096            if (classDescriptor != null) {
097                PsiElement element = BindingContextUtils.classDescriptorToDeclaration(trace.getBindingContext(), classDescriptor);
098                if (element != null && PsiSearchScopeUtil.isInScope(searchScope, element)) {
099                    return Collections.singletonList((JetClassOrObject) element);
100                }
101            }
102    
103            if (JvmAbi.isClassObjectFqName(fqName)) {
104                Collection<JetClassOrObject> parentClasses = findClassOrObjectDeclarations(fqName.parent(), searchScope);
105                return ContainerUtil.mapNotNull(parentClasses,
106                                                new Function<JetClassOrObject, JetClassOrObject>() {
107                                                    @Override
108                                                    public JetClassOrObject fun(JetClassOrObject classOrObject) {
109                                                        if (classOrObject instanceof JetClass) {
110                                                            JetClass jetClass = (JetClass) classOrObject;
111                                                            JetClassObject classObject = jetClass.getClassObject();
112                                                            if (classObject != null) {
113                                                                return classObject.getObjectDeclaration();
114                                                            }
115                                                        }
116                                                        return null;
117                                                    }
118                                                });
119            }
120    
121            return Collections.emptyList();
122        }
123    
124        @NotNull
125        @Override
126        public Collection<JetFile> findFilesForPackage(@NotNull FqName fqName, @NotNull final GlobalSearchScope searchScope) {
127            NamespaceDescriptor namespaceDescriptor = getTrace().get(BindingContext.FQNAME_TO_NAMESPACE_DESCRIPTOR, fqName);
128            if (namespaceDescriptor != null) {
129                Collection<JetFile> files = getTrace().get(BindingContext.NAMESPACE_TO_FILES, namespaceDescriptor);
130                if (files != null) {
131                    return Collections2.filter(files, new Predicate<JetFile>() {
132                        @Override
133                        public boolean apply(JetFile input) {
134                            return PsiSearchScopeUtil.isInScope(searchScope, input);
135                        }
136                    });
137                }
138            }
139            return Collections.emptyList();
140        }
141    
142        @NotNull
143        @Override
144        public Collection<JetClassOrObject> findClassOrObjectDeclarationsInPackage(
145                @NotNull FqName packageFqName, @NotNull GlobalSearchScope searchScope
146        ) {
147            Collection<JetFile> files = findFilesForPackage(packageFqName, searchScope);
148            List<JetClassOrObject> result = new SmartList<JetClassOrObject>();
149            for (JetFile file : files) {
150                for (JetDeclaration declaration : file.getDeclarations()) {
151                    if (declaration instanceof JetClassOrObject) {
152                        result.add((JetClassOrObject) declaration);
153                    }
154                }
155            }
156            return result;
157        }
158    
159        @Override
160        public boolean packageExists(
161                @NotNull FqName fqName, @NotNull GlobalSearchScope scope
162        ) {
163            return getTrace().get(BindingContext.FQNAME_TO_NAMESPACE_DESCRIPTOR, fqName) != null;
164        }
165    
166        @NotNull
167        @Override
168        public Collection<FqName> getSubPackages(@NotNull FqName fqn, @NotNull GlobalSearchScope scope) {
169            NamespaceDescriptor namespaceDescriptor = getTrace().get(BindingContext.FQNAME_TO_NAMESPACE_DESCRIPTOR, fqn);
170            if (namespaceDescriptor == null) return Collections.emptyList();
171    
172            Collection<DeclarationDescriptor> allDescriptors = namespaceDescriptor.getMemberScope().getAllDescriptors();
173            return ContainerUtil.mapNotNull(allDescriptors, new Function<DeclarationDescriptor, FqName>() {
174                @Override
175                public FqName fun(DeclarationDescriptor input) {
176                    if (input instanceof NamespaceDescriptor) {
177                        return DescriptorUtils.getFQName(input).toSafe();
178                    }
179                    return null;
180                }
181            });
182        }
183    
184        @Nullable
185        @Override
186        public PsiClass getPsiClass(@NotNull JetClassOrObject classOrObject) {
187            return  KotlinLightClassForExplicitDeclaration.create(classOrObject.getManager(), classOrObject);
188        }
189    }