001 /* 002 * Copyright 2010-2014 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.psi.util.CachedValue; 030 import com.intellij.psi.util.CachedValueProvider; 031 import com.intellij.psi.util.CachedValuesManager; 032 import com.intellij.psi.util.PsiModificationTracker; 033 import com.intellij.util.SmartList; 034 import com.intellij.util.containers.SLRUCache; 035 import org.jetbrains.annotations.NotNull; 036 import org.jetbrains.annotations.Nullable; 037 import org.jetbrains.jet.lang.psi.*; 038 import org.jetbrains.jet.lang.resolve.java.JavaPsiFacadeKotlinHacks; 039 import org.jetbrains.jet.lang.resolve.java.PackageClassUtils; 040 import org.jetbrains.jet.lang.resolve.kotlin.PackagePartClassUtils; 041 import org.jetbrains.jet.lang.resolve.name.FqName; 042 import org.jetbrains.jet.lang.resolve.name.NamePackage; 043 044 import java.util.Collection; 045 import java.util.List; 046 import java.util.Set; 047 048 public class JavaElementFinder extends PsiElementFinder implements JavaPsiFacadeKotlinHacks.KotlinFinderMarker { 049 050 @NotNull 051 public static JavaElementFinder getInstance(@NotNull Project project) { 052 PsiElementFinder[] extensions = Extensions.getArea(project).getExtensionPoint(PsiElementFinder.EP_NAME).getExtensions(); 053 for (PsiElementFinder extension : extensions) { 054 if (extension instanceof JavaElementFinder) { 055 return (JavaElementFinder) extension; 056 } 057 } 058 throw new IllegalStateException(JavaElementFinder.class.getSimpleName() + " is not found for project " + project); 059 } 060 061 private final Project project; 062 private final PsiManager psiManager; 063 private final LightClassGenerationSupport lightClassGenerationSupport; 064 065 private final CachedValue<SLRUCache<FindClassesRequest, PsiClass[]>> findClassesCache; 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 this.findClassesCache = CachedValuesManager.getManager(project).createCachedValue( 075 new CachedValueProvider<SLRUCache<FindClassesRequest, PsiClass[]>>() { 076 @Nullable 077 @Override 078 public Result<SLRUCache<FindClassesRequest, PsiClass[]>> compute() { 079 return new Result<SLRUCache<FindClassesRequest, PsiClass[]>>( 080 new SLRUCache<FindClassesRequest, PsiClass[]>(30, 10) { 081 @NotNull 082 @Override 083 public PsiClass[] createValue(FindClassesRequest key) { 084 return doFindClasses(key.fqName, key.scope); 085 } 086 }, 087 PsiModificationTracker.OUT_OF_CODE_BLOCK_MODIFICATION_COUNT 088 ); 089 } 090 }, 091 false 092 ); 093 } 094 095 @Override 096 public PsiClass findClass(@NotNull String qualifiedName, @NotNull GlobalSearchScope scope) { 097 PsiClass[] allClasses = findClasses(qualifiedName, scope); 098 return allClasses.length > 0 ? allClasses[0] : null; 099 } 100 101 @NotNull 102 @Override 103 public PsiClass[] findClasses(@NotNull String qualifiedNameString, @NotNull GlobalSearchScope scope) { 104 SLRUCache<FindClassesRequest, PsiClass[]> value = findClassesCache.getValue(); 105 synchronized (value) { 106 return value.get(new FindClassesRequest(qualifiedNameString, scope)); 107 } 108 } 109 110 private PsiClass[] doFindClasses(String qualifiedNameString, GlobalSearchScope scope) { 111 if (!NamePackage.isValidJavaFqName(qualifiedNameString)) { 112 return PsiClass.EMPTY_ARRAY; 113 } 114 115 List<PsiClass> answer = new SmartList<PsiClass>(); 116 117 FqName qualifiedName = new FqName(qualifiedNameString); 118 119 findClassesAndObjects(qualifiedName, scope, answer); 120 121 if (PackageClassUtils.isPackageClassFqName(qualifiedName)) { 122 findPackageClass(qualifiedName.parent(), scope, answer); 123 } 124 125 return answer.toArray(new PsiClass[answer.size()]); 126 } 127 128 // Finds explicitly declared classes and objects, not package classes 129 private void findClassesAndObjects(FqName qualifiedName, GlobalSearchScope scope, List<PsiClass> answer) { 130 Collection<JetClassOrObject> classOrObjectDeclarations = 131 lightClassGenerationSupport.findClassOrObjectDeclarations(qualifiedName, scope); 132 133 for (JetClassOrObject declaration : classOrObjectDeclarations) { 134 if (!(declaration instanceof JetEnumEntry)) { 135 PsiClass lightClass = LightClassUtil.getPsiClass(declaration); 136 if (lightClass != null) { 137 answer.add(lightClass); 138 } 139 } 140 } 141 } 142 143 private void findPackageClass(FqName qualifiedName, GlobalSearchScope scope, List<PsiClass> answer) { 144 Collection<JetFile> filesForPackage = lightClassGenerationSupport.findFilesForPackage(qualifiedName, scope); 145 if (PackagePartClassUtils.getPackageFilesWithCallables(filesForPackage).isEmpty()) return; 146 147 KotlinLightClassForPackage lightClass = KotlinLightClassForPackage.create(psiManager, qualifiedName, scope, filesForPackage); 148 if (lightClass == null) return; 149 150 answer.add(lightClass); 151 152 if (filesForPackage.size() > 1) { 153 for (JetFile file : filesForPackage) { 154 answer.add(new FakeLightClassForFileOfPackage(psiManager, lightClass, file)); 155 } 156 } 157 } 158 159 @NotNull 160 @Override 161 public Set<String> getClassNames(@NotNull PsiPackage psiPackage, @NotNull GlobalSearchScope scope) { 162 FqName packageFQN = new FqName(psiPackage.getQualifiedName()); 163 164 Collection<JetClassOrObject> declarations = lightClassGenerationSupport.findClassOrObjectDeclarationsInPackage(packageFQN, scope); 165 166 Set<String> answer = Sets.newHashSet(); 167 answer.add(PackageClassUtils.getPackageClassName(packageFQN)); 168 169 for (JetClassOrObject declaration : declarations) { 170 String name = declaration.getName(); 171 if (name != null) { 172 answer.add(name); 173 } 174 } 175 176 return answer; 177 } 178 179 @Override 180 public PsiPackage findPackage(@NotNull String qualifiedNameString) { 181 if (!NamePackage.isValidJavaFqName(qualifiedNameString)) { 182 return null; 183 } 184 185 FqName fqName = new FqName(qualifiedNameString); 186 187 // allScope() because the contract says that the whole project 188 GlobalSearchScope allScope = GlobalSearchScope.allScope(project); 189 if (lightClassGenerationSupport.packageExists(fqName, allScope)) { 190 return new JetLightPackage(psiManager, fqName, allScope); 191 } 192 193 return null; 194 } 195 196 @NotNull 197 @Override 198 public PsiPackage[] getSubPackages(@NotNull PsiPackage psiPackage, @NotNull final GlobalSearchScope scope) { 199 FqName packageFQN = new FqName(psiPackage.getQualifiedName()); 200 201 Collection<FqName> subpackages = lightClassGenerationSupport.getSubPackages(packageFQN, scope); 202 203 Collection<PsiPackage> answer = Collections2.transform(subpackages, new Function<FqName, PsiPackage>() { 204 @Override 205 public PsiPackage apply(@Nullable FqName input) { 206 return new JetLightPackage(psiManager, input, scope); 207 } 208 }); 209 210 return answer.toArray(new PsiPackage[answer.size()]); 211 } 212 213 @NotNull 214 @Override 215 public PsiClass[] getClasses(@NotNull PsiPackage psiPackage, @NotNull GlobalSearchScope scope) { 216 List<PsiClass> answer = new SmartList<PsiClass>(); 217 FqName packageFQN = new FqName(psiPackage.getQualifiedName()); 218 219 findPackageClass(packageFQN, scope, answer); 220 221 Collection<JetClassOrObject> declarations = lightClassGenerationSupport.findClassOrObjectDeclarationsInPackage(packageFQN, scope); 222 for (JetClassOrObject declaration : declarations) { 223 PsiClass aClass = LightClassUtil.getPsiClass(declaration); 224 if (aClass != null) { 225 answer.add(aClass); 226 } 227 } 228 229 return answer.toArray(new PsiClass[answer.size()]); 230 } 231 232 private static class FindClassesRequest { 233 private final String fqName; 234 private final GlobalSearchScope scope; 235 236 private FindClassesRequest(@NotNull String fqName, @NotNull GlobalSearchScope scope) { 237 this.fqName = fqName; 238 this.scope = scope; 239 } 240 241 @Override 242 public boolean equals(Object o) { 243 if (this == o) return true; 244 if (o == null || getClass() != o.getClass()) return false; 245 246 FindClassesRequest request = (FindClassesRequest) o; 247 248 if (!fqName.equals(request.fqName)) return false; 249 if (!scope.equals(request.scope)) return false; 250 251 return true; 252 } 253 254 @Override 255 public int hashCode() { 256 int result = fqName.hashCode(); 257 result = 31 * result + (scope.hashCode()); 258 return result; 259 } 260 } 261 } 262