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