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