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.asJava;
018    
019    import com.google.common.base.Function;
020    import com.google.common.collect.Collections2;
021    import com.google.common.collect.Sets;
022    import com.intellij.openapi.extensions.Extensions;
023    import com.intellij.openapi.project.Project;
024    import com.intellij.psi.PsiClass;
025    import com.intellij.psi.PsiElementFinder;
026    import com.intellij.psi.PsiManager;
027    import com.intellij.psi.PsiPackage;
028    import com.intellij.psi.search.GlobalSearchScope;
029    import com.intellij.util.SmartList;
030    import org.jetbrains.annotations.NotNull;
031    import org.jetbrains.annotations.Nullable;
032    import org.jetbrains.jet.codegen.NamespaceCodegen;
033    import org.jetbrains.jet.lang.psi.JetClassOrObject;
034    import org.jetbrains.jet.lang.psi.JetEnumEntry;
035    import org.jetbrains.jet.lang.psi.JetFile;
036    import org.jetbrains.jet.lang.resolve.java.JavaPsiFacadeKotlinHacks;
037    import org.jetbrains.jet.lang.resolve.java.PackageClassUtils;
038    import org.jetbrains.jet.lang.resolve.name.FqName;
039    import org.jetbrains.jet.util.QualifiedNamesUtil;
040    
041    import java.util.Collection;
042    import java.util.List;
043    import java.util.Set;
044    
045    public class JavaElementFinder extends PsiElementFinder implements JavaPsiFacadeKotlinHacks.KotlinFinderMarker {
046    
047        @NotNull
048        public static JavaElementFinder getInstance(@NotNull Project project) {
049            PsiElementFinder[] extensions = Extensions.getArea(project).getExtensionPoint(PsiElementFinder.EP_NAME).getExtensions();
050            for (PsiElementFinder extension : extensions) {
051                if (extension instanceof JavaElementFinder) {
052                    return (JavaElementFinder) extension;
053                }
054            }
055            throw new IllegalStateException(JavaElementFinder.class.getSimpleName() + " is not found for project " + project);
056        }
057    
058        private final Project project;
059        private final PsiManager psiManager;
060        private final LightClassGenerationSupport lightClassGenerationSupport;
061    
062        public JavaElementFinder(
063                @NotNull Project project,
064                @NotNull LightClassGenerationSupport lightClassGenerationSupport
065        ) {
066            this.project = project;
067            this.psiManager = PsiManager.getInstance(project);
068            this.lightClassGenerationSupport = lightClassGenerationSupport;
069        }
070    
071        @Override
072        public PsiClass findClass(@NotNull String qualifiedName, @NotNull GlobalSearchScope scope) {
073            PsiClass[] allClasses = findClasses(qualifiedName, scope);
074            return allClasses.length > 0 ? allClasses[0] : null;
075        }
076    
077        @NotNull
078        @Override
079        public PsiClass[] findClasses(@NotNull String qualifiedNameString, @NotNull GlobalSearchScope scope) {
080            if (!QualifiedNamesUtil.isValidJavaFqName(qualifiedNameString)) {
081                return PsiClass.EMPTY_ARRAY;
082            }
083    
084            List<PsiClass> answer = new SmartList<PsiClass>();
085    
086            FqName qualifiedName = new FqName(qualifiedNameString);
087    
088            findClassesAndObjects(qualifiedName, scope, answer);
089    
090            if (PackageClassUtils.isPackageClassFqName(qualifiedName)) {
091                findPackageClass(qualifiedName.parent(), scope, answer);
092            }
093    
094            return answer.toArray(new PsiClass[answer.size()]);
095        }
096    
097        // Finds explicitly declared classes and objects, not package classes
098        private void findClassesAndObjects(FqName qualifiedName, GlobalSearchScope scope, List<PsiClass> answer) {
099            Collection<JetClassOrObject> classOrObjectDeclarations =
100                    lightClassGenerationSupport.findClassOrObjectDeclarations(qualifiedName, scope);
101    
102            for (JetClassOrObject declaration : classOrObjectDeclarations) {
103                if (!(declaration instanceof JetEnumEntry)) {
104                    PsiClass lightClass = LightClassUtil.getPsiClass(declaration);
105                    if (lightClass != null) {
106                        answer.add(lightClass);
107                    }
108                }
109            }
110        }
111    
112        private void findPackageClass(FqName qualifiedName, GlobalSearchScope scope, List<PsiClass> answer) {
113            Collection<JetFile> filesForPackage = lightClassGenerationSupport.findFilesForPackage(qualifiedName, scope);
114    
115            if (!filesForPackage.isEmpty() && NamespaceCodegen.shouldGenerateNSClass(filesForPackage)) {
116                KotlinLightClassForPackage lightClass = KotlinLightClassForPackage.create(psiManager, qualifiedName, scope, filesForPackage);
117                if (lightClass != null) {
118                    answer.add(lightClass);
119    
120                    if (filesForPackage.size() > 1) {
121                        for (JetFile file : filesForPackage) {
122                            FakeLightClassForFileOfPackage fakeLightClass = new FakeLightClassForFileOfPackage(psiManager, lightClass, file);
123                            answer.add(fakeLightClass);
124                        }
125                    }
126                }
127            }
128        }
129    
130        @NotNull
131        @Override
132        public Set<String> getClassNames(@NotNull PsiPackage psiPackage, @NotNull GlobalSearchScope scope) {
133            FqName packageFQN = new FqName(psiPackage.getQualifiedName());
134    
135            Collection<JetClassOrObject> declarations = lightClassGenerationSupport.findClassOrObjectDeclarationsInPackage(packageFQN, scope);
136    
137            Set<String> answer = Sets.newHashSet();
138            answer.add(PackageClassUtils.getPackageClassName(packageFQN));
139    
140            for (JetClassOrObject declaration : declarations) {
141                String name = declaration.getName();
142                if (name != null) {
143                    answer.add(name);
144                }
145            }
146    
147            return answer;
148        }
149    
150        @Override
151        public PsiPackage findPackage(@NotNull String qualifiedNameString) {
152            if (!QualifiedNamesUtil.isValidJavaFqName(qualifiedNameString)) {
153                return null;
154            }
155    
156            FqName fqName = new FqName(qualifiedNameString);
157    
158            // allScope() because the contract says that the whole project
159            GlobalSearchScope allScope = GlobalSearchScope.allScope(project);
160            if (lightClassGenerationSupport.packageExists(fqName, allScope)) {
161                return new JetLightPackage(psiManager, fqName, allScope);
162            }
163    
164            return null;
165        }
166    
167        @NotNull
168        @Override
169        public PsiPackage[] getSubPackages(@NotNull PsiPackage psiPackage, @NotNull final GlobalSearchScope scope) {
170            FqName packageFQN = new FqName(psiPackage.getQualifiedName());
171    
172            Collection<FqName> subpackages = lightClassGenerationSupport.getSubPackages(packageFQN, scope);
173    
174            Collection<PsiPackage> answer = Collections2.transform(subpackages, new Function<FqName, PsiPackage>() {
175                @Override
176                public PsiPackage apply(@Nullable FqName input) {
177                    return new JetLightPackage(psiManager, input, scope);
178                }
179            });
180    
181            return answer.toArray(new PsiPackage[answer.size()]);
182        }
183    
184        @NotNull
185        @Override
186        public PsiClass[] getClasses(@NotNull PsiPackage psiPackage, @NotNull GlobalSearchScope scope) {
187            List<PsiClass> answer = new SmartList<PsiClass>();
188            FqName packageFQN = new FqName(psiPackage.getQualifiedName());
189    
190            findPackageClass(packageFQN, scope, answer);
191    
192            Collection<JetClassOrObject> declarations = lightClassGenerationSupport.findClassOrObjectDeclarationsInPackage(packageFQN, scope);
193            for (JetClassOrObject declaration : declarations) {
194                PsiClass aClass = LightClassUtil.getPsiClass(declaration);
195                if (aClass != null) {
196                    answer.add(aClass);
197                }
198            }
199    
200            return answer.toArray(new PsiClass[answer.size()]);
201        }
202    }
203