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