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