001 /* 002 * Copyright 2010-2016 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.kotlin.asJava.finder; 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.openapi.util.Condition; 025 import com.intellij.openapi.vfs.VirtualFile; 026 import com.intellij.psi.*; 027 import com.intellij.psi.search.GlobalSearchScope; 028 import com.intellij.psi.util.PsiUtilCore; 029 import com.intellij.util.SmartList; 030 import com.intellij.util.containers.ContainerUtil; 031 import org.jetbrains.annotations.NotNull; 032 import org.jetbrains.annotations.Nullable; 033 import org.jetbrains.kotlin.asJava.LightClassGenerationSupport; 034 import org.jetbrains.kotlin.load.java.JvmAbi; 035 import org.jetbrains.kotlin.name.FqName; 036 import org.jetbrains.kotlin.name.FqNamesUtilKt; 037 import org.jetbrains.kotlin.psi.KtClass; 038 import org.jetbrains.kotlin.psi.KtClassOrObject; 039 import org.jetbrains.kotlin.psi.KtEnumEntry; 040 import org.jetbrains.kotlin.psi.KtFile; 041 import org.jetbrains.kotlin.resolve.jvm.KotlinFinderMarker; 042 043 import java.util.Collection; 044 import java.util.Comparator; 045 import java.util.List; 046 import java.util.Set; 047 048 import static org.jetbrains.kotlin.asJava.LightClassUtilsKt.toLightClass; 049 050 public class JavaElementFinder extends PsiElementFinder implements KotlinFinderMarker { 051 052 @NotNull 053 public static JavaElementFinder getInstance(@NotNull Project project) { 054 PsiElementFinder[] extensions = Extensions.getArea(project).getExtensionPoint(PsiElementFinder.EP_NAME).getExtensions(); 055 for (PsiElementFinder extension : extensions) { 056 if (extension instanceof JavaElementFinder) { 057 return (JavaElementFinder) extension; 058 } 059 } 060 throw new IllegalStateException(JavaElementFinder.class.getSimpleName() + " is not found for project " + project); 061 } 062 063 private final Project project; 064 private final PsiManager psiManager; 065 private final LightClassGenerationSupport lightClassGenerationSupport; 066 067 public JavaElementFinder( 068 @NotNull Project project, 069 @NotNull LightClassGenerationSupport lightClassGenerationSupport 070 ) { 071 this.project = project; 072 this.psiManager = PsiManager.getInstance(project); 073 this.lightClassGenerationSupport = lightClassGenerationSupport; 074 } 075 076 @Override 077 public PsiClass findClass(@NotNull String qualifiedName, @NotNull GlobalSearchScope scope) { 078 PsiClass[] allClasses = findClasses(qualifiedName, scope); 079 return allClasses.length > 0 ? allClasses[0] : null; 080 } 081 082 @NotNull 083 @Override 084 public PsiClass[] findClasses(@NotNull String qualifiedNameString, @NotNull GlobalSearchScope scope) { 085 if (!FqNamesUtilKt.isValidJavaFqName(qualifiedNameString)) { 086 return PsiClass.EMPTY_ARRAY; 087 } 088 089 List<PsiClass> answer = new SmartList<PsiClass>(); 090 091 FqName qualifiedName = new FqName(qualifiedNameString); 092 093 findClassesAndObjects(qualifiedName, scope, answer); 094 095 answer.addAll(lightClassGenerationSupport.getFacadeClasses(qualifiedName, scope)); 096 answer.addAll(lightClassGenerationSupport.getMultifilePartClasses(qualifiedName, scope)); 097 098 return sortByClasspath(answer, scope).toArray(new PsiClass[answer.size()]); 099 } 100 101 // Finds explicitly declared classes and objects, not package classes 102 // Also DefaultImpls classes of interfaces 103 private void findClassesAndObjects(FqName qualifiedName, GlobalSearchScope scope, List<PsiClass> answer) { 104 findInterfaceDefaultImpls(qualifiedName, scope, answer); 105 106 Collection<KtClassOrObject> classOrObjectDeclarations = 107 lightClassGenerationSupport.findClassOrObjectDeclarations(qualifiedName, scope); 108 109 for (KtClassOrObject declaration : classOrObjectDeclarations) { 110 if (!(declaration instanceof KtEnumEntry)) { 111 PsiClass lightClass = toLightClass(declaration); 112 if (lightClass != null) { 113 answer.add(lightClass); 114 } 115 } 116 } 117 } 118 119 private void findInterfaceDefaultImpls(FqName qualifiedName, GlobalSearchScope scope, List<PsiClass> answer) { 120 if (qualifiedName.isRoot()) return; 121 122 if (!qualifiedName.shortName().asString().equals(JvmAbi.DEFAULT_IMPLS_CLASS_NAME)) return; 123 124 for (KtClassOrObject classOrObject : lightClassGenerationSupport.findClassOrObjectDeclarations(qualifiedName.parent(), scope)) { 125 //NOTE: can't filter out more interfaces right away because decompiled declarations do not have member bodies 126 if (classOrObject instanceof KtClass && ((KtClass) classOrObject).isInterface()) { 127 PsiClass interfaceClass = toLightClass(classOrObject); 128 if (interfaceClass != null) { 129 PsiClass implsClass = interfaceClass.findInnerClassByName(JvmAbi.DEFAULT_IMPLS_CLASS_NAME, false); 130 if (implsClass != null) { 131 answer.add(implsClass); 132 } 133 } 134 } 135 } 136 } 137 138 @NotNull 139 @Override 140 public Set<String> getClassNames(@NotNull PsiPackage psiPackage, @NotNull GlobalSearchScope scope) { 141 FqName packageFQN = new FqName(psiPackage.getQualifiedName()); 142 143 Collection<KtClassOrObject> declarations = lightClassGenerationSupport.findClassOrObjectDeclarationsInPackage(packageFQN, scope); 144 145 Set<String> answer = Sets.newHashSet(); 146 answer.addAll(lightClassGenerationSupport.getFacadeNames(packageFQN, scope)); 147 148 for (KtClassOrObject declaration : declarations) { 149 String name = declaration.getName(); 150 if (name != null) { 151 answer.add(name); 152 } 153 } 154 155 return answer; 156 } 157 158 @Override 159 public PsiPackage findPackage(@NotNull String qualifiedNameString) { 160 if (!FqNamesUtilKt.isValidJavaFqName(qualifiedNameString)) { 161 return null; 162 } 163 164 FqName fqName = new FqName(qualifiedNameString); 165 166 // allScope() because the contract says that the whole project 167 GlobalSearchScope allScope = GlobalSearchScope.allScope(project); 168 if (lightClassGenerationSupport.packageExists(fqName, allScope)) { 169 return new KtLightPackage(psiManager, fqName, allScope); 170 } 171 172 return null; 173 } 174 175 @NotNull 176 @Override 177 public PsiPackage[] getSubPackages(@NotNull PsiPackage psiPackage, @NotNull final GlobalSearchScope scope) { 178 FqName packageFQN = new FqName(psiPackage.getQualifiedName()); 179 180 Collection<FqName> subpackages = lightClassGenerationSupport.getSubPackages(packageFQN, scope); 181 182 Collection<PsiPackage> answer = Collections2.transform(subpackages, new Function<FqName, PsiPackage>() { 183 @Override 184 public PsiPackage apply(@Nullable FqName input) { 185 return new KtLightPackage(psiManager, input, scope); 186 } 187 }); 188 189 return answer.toArray(new PsiPackage[answer.size()]); 190 } 191 192 @NotNull 193 @Override 194 public PsiClass[] getClasses(@NotNull PsiPackage psiPackage, @NotNull GlobalSearchScope scope) { 195 List<PsiClass> answer = new SmartList<PsiClass>(); 196 FqName packageFQN = new FqName(psiPackage.getQualifiedName()); 197 198 answer.addAll(lightClassGenerationSupport.getFacadeClassesInPackage(packageFQN, scope)); 199 200 Collection<KtClassOrObject> declarations = lightClassGenerationSupport.findClassOrObjectDeclarationsInPackage(packageFQN, scope); 201 for (KtClassOrObject declaration : declarations) { 202 PsiClass aClass = toLightClass(declaration); 203 if (aClass != null) { 204 answer.add(aClass); 205 } 206 } 207 208 return sortByClasspath(answer, scope).toArray(new PsiClass[answer.size()]); 209 } 210 211 @Override 212 @NotNull 213 public PsiFile[] getPackageFiles(@NotNull PsiPackage psiPackage, @NotNull GlobalSearchScope scope) { 214 FqName packageFQN = new FqName(psiPackage.getQualifiedName()); 215 Collection<KtFile> result = lightClassGenerationSupport.findFilesForPackage(packageFQN, scope); 216 return result.toArray(new PsiFile[result.size()]); 217 } 218 219 @Override 220 @Nullable 221 public Condition<PsiFile> getPackageFilesFilter(@NotNull final PsiPackage psiPackage, @NotNull GlobalSearchScope scope) { 222 return new Condition<PsiFile>() { 223 @Override 224 public boolean value(PsiFile input) { 225 if (!(input instanceof KtFile)) { 226 return true; 227 } 228 return psiPackage.getQualifiedName().equals(((KtFile) input).getPackageFqName().asString()); 229 } 230 }; 231 } 232 233 @NotNull 234 public static Comparator<PsiElement> byClasspathComparator(@NotNull final GlobalSearchScope searchScope) { 235 return new Comparator<PsiElement>() { 236 @Override 237 public int compare(@NotNull PsiElement o1, @NotNull PsiElement o2) { 238 VirtualFile f1 = PsiUtilCore.getVirtualFile(o1); 239 VirtualFile f2 = PsiUtilCore.getVirtualFile(o2); 240 if (f1 == f2) return 0; 241 if (f1 == null) return -1; 242 if (f2 == null) return 1; 243 return searchScope.compare(f2, f1); 244 } 245 }; 246 } 247 248 private static Collection<PsiClass> sortByClasspath(@NotNull List<PsiClass> classes, @NotNull GlobalSearchScope searchScope) { 249 if (classes.size() > 1) { 250 ContainerUtil.quickSort(classes, byClasspathComparator(searchScope)); 251 } 252 253 return classes; 254 } 255 }