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