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.asJava;
018
019import com.google.common.base.Function;
020import com.google.common.collect.Collections2;
021import com.google.common.collect.Sets;
022import com.intellij.openapi.extensions.Extensions;
023import com.intellij.openapi.project.Project;
024import com.intellij.psi.PsiClass;
025import com.intellij.psi.PsiElementFinder;
026import com.intellij.psi.PsiManager;
027import com.intellij.psi.PsiPackage;
028import com.intellij.psi.search.GlobalSearchScope;
029import com.intellij.util.SmartList;
030import org.jetbrains.annotations.NotNull;
031import org.jetbrains.annotations.Nullable;
032import org.jetbrains.jet.codegen.NamespaceCodegen;
033import org.jetbrains.jet.lang.psi.*;
034import org.jetbrains.jet.lang.resolve.java.JavaPsiFacadeKotlinHacks;
035import org.jetbrains.jet.lang.resolve.java.PackageClassUtils;
036import org.jetbrains.jet.lang.resolve.name.FqName;
037import org.jetbrains.jet.util.QualifiedNamesUtil;
038
039import java.util.Collection;
040import java.util.List;
041import java.util.Set;
042
043public 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