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 017package org.jetbrains.jet.asJava; 018 019import com.google.common.base.Function; 020import com.google.common.collect.Collections2; 021import com.google.common.collect.Sets; 022import com.intellij.openapi.extensions.Extensions; 023import com.intellij.openapi.project.Project; 024import com.intellij.psi.PsiClass; 025import com.intellij.psi.PsiElementFinder; 026import com.intellij.psi.PsiManager; 027import com.intellij.psi.PsiPackage; 028import com.intellij.psi.search.GlobalSearchScope; 029import com.intellij.util.SmartList; 030import org.jetbrains.annotations.NotNull; 031import org.jetbrains.annotations.Nullable; 032import org.jetbrains.jet.codegen.NamespaceCodegen; 033import org.jetbrains.jet.lang.psi.*; 034import org.jetbrains.jet.lang.resolve.java.JavaPsiFacadeKotlinHacks; 035import org.jetbrains.jet.lang.resolve.java.PackageClassUtils; 036import org.jetbrains.jet.lang.resolve.name.FqName; 037import org.jetbrains.jet.util.QualifiedNamesUtil; 038 039import java.util.Collection; 040import java.util.List; 041import java.util.Set; 042 043public class JavaElementFinder extends PsiElementFinder implements JavaPsiFacadeKotlinHacks.KotlinFinderMarker { 044 045 @NotNull 046 public static JavaElementFinder getInstance(@NotNull Project project) { 047 PsiElementFinder[] extensions = Extensions.getArea(project).getExtensionPoint(PsiElementFinder.EP_NAME).getExtensions(); 048 for (PsiElementFinder extension : extensions) { 049 if (extension instanceof JavaElementFinder) { 050 return (JavaElementFinder) extension; 051 } 052 } 053 throw new IllegalStateException(JavaElementFinder.class.getSimpleName() + " is not found for project " + project); 054 } 055 056 private final Project project; 057 private final PsiManager psiManager; 058 private final LightClassGenerationSupport lightClassGenerationSupport; 059 060 public JavaElementFinder( 061 @NotNull Project project, 062 @NotNull LightClassGenerationSupport lightClassGenerationSupport 063 ) { 064 this.project = project; 065 this.psiManager = PsiManager.getInstance(project); 066 this.lightClassGenerationSupport = lightClassGenerationSupport; 067 } 068 069 @Override 070 public PsiClass findClass(@NotNull String qualifiedName, @NotNull GlobalSearchScope scope) { 071 PsiClass[] allClasses = findClasses(qualifiedName, scope); 072 return allClasses.length > 0 ? allClasses[0] : null; 073 } 074 075 @NotNull 076 @Override 077 public PsiClass[] findClasses(@NotNull String qualifiedNameString, @NotNull GlobalSearchScope scope) { 078 if (!QualifiedNamesUtil.isValidJavaFqName(qualifiedNameString)) { 079 return PsiClass.EMPTY_ARRAY; 080 } 081 082 List<PsiClass> answer = new SmartList<PsiClass>(); 083 084 FqName qualifiedName = new FqName(qualifiedNameString); 085 086 findClassesAndObjects(qualifiedName, scope, answer); 087 088 if (PackageClassUtils.isPackageClass(qualifiedName)) { 089 findPackageClass(qualifiedName.parent(), scope, answer); 090 } 091 092 return answer.toArray(new PsiClass[answer.size()]); 093 } 094 095 // Finds explicitly declared classes and objects, not package classes 096 private void findClassesAndObjects(FqName qualifiedName, GlobalSearchScope scope, List<PsiClass> answer) { 097 Collection<JetClassOrObject> classOrObjectDeclarations = 098 lightClassGenerationSupport.findClassOrObjectDeclarations(qualifiedName, scope); 099 100 for (JetClassOrObject declaration : classOrObjectDeclarations) { 101 if (!(declaration instanceof JetEnumEntry)) { 102 PsiClass lightClass = LightClassUtil.getPsiClass(declaration); 103 if (lightClass != null) { 104 answer.add(lightClass); 105 } 106 } 107 } 108 } 109 110 private void findPackageClass(FqName qualifiedName, GlobalSearchScope scope, List<PsiClass> answer) { 111 Collection<JetFile> filesForPackage = lightClassGenerationSupport.findFilesForPackage(qualifiedName, scope); 112 113 if (!filesForPackage.isEmpty() && NamespaceCodegen.shouldGenerateNSClass(filesForPackage)) { 114 KotlinLightClassForPackage lightClass = KotlinLightClassForPackage.create(psiManager, qualifiedName, scope, filesForPackage); 115 if (lightClass != null) { 116 answer.add(lightClass); 117 118 if (filesForPackage.size() > 1) { 119 for (JetFile file : filesForPackage) { 120 FakeLightClassForFileOfPackage fakeLightClass = new FakeLightClassForFileOfPackage(psiManager, lightClass, file); 121 answer.add(fakeLightClass); 122 } 123 } 124 } 125 } 126 } 127 128 @NotNull 129 @Override 130 public Set<String> getClassNames(@NotNull PsiPackage psiPackage, @NotNull GlobalSearchScope scope) { 131 FqName packageFQN = new FqName(psiPackage.getQualifiedName()); 132 133 Collection<JetClassOrObject> declarations = lightClassGenerationSupport.findClassOrObjectDeclarationsInPackage(packageFQN, scope); 134 135 Set<String> answer = Sets.newHashSet(); 136 answer.add(PackageClassUtils.getPackageClassName(packageFQN)); 137 138 for (JetClassOrObject declaration : declarations) { 139 String name = declaration.getName(); 140 if (name != null) { 141 answer.add(name); 142 } 143 } 144 145 return answer; 146 } 147 148 @Override 149 public PsiPackage findPackage(@NotNull String qualifiedNameString) { 150 if (!QualifiedNamesUtil.isValidJavaFqName(qualifiedNameString)) { 151 return null; 152 } 153 154 FqName fqName = new FqName(qualifiedNameString); 155 156 // allScope() because the contract says that the whole project 157 GlobalSearchScope allScope = GlobalSearchScope.allScope(project); 158 if (lightClassGenerationSupport.packageExists(fqName, allScope)) { 159 return new JetLightPackage(psiManager, fqName, allScope); 160 } 161 162 return null; 163 } 164 165 @NotNull 166 @Override 167 public PsiPackage[] getSubPackages(@NotNull PsiPackage psiPackage, @NotNull final GlobalSearchScope scope) { 168 FqName packageFQN = new FqName(psiPackage.getQualifiedName()); 169 170 Collection<FqName> subpackages = lightClassGenerationSupport.getSubPackages(packageFQN, scope); 171 172 Collection<PsiPackage> answer = Collections2.transform(subpackages, new Function<FqName, PsiPackage>() { 173 @Override 174 public PsiPackage apply(@Nullable FqName input) { 175 return new JetLightPackage(psiManager, input, scope); 176 } 177 }); 178 179 return answer.toArray(new PsiPackage[answer.size()]); 180 } 181 182 @NotNull 183 @Override 184 public PsiClass[] getClasses(@NotNull PsiPackage psiPackage, @NotNull GlobalSearchScope scope) { 185 List<PsiClass> answer = new SmartList<PsiClass>(); 186 FqName packageFQN = new FqName(psiPackage.getQualifiedName()); 187 188 findPackageClass(packageFQN, scope, answer); 189 190 Collection<JetClassOrObject> declarations = lightClassGenerationSupport.findClassOrObjectDeclarationsInPackage(packageFQN, scope); 191 for (JetClassOrObject declaration : declarations) { 192 PsiClass aClass = LightClassUtil.getPsiClass(declaration); 193 if (aClass != null) { 194 answer.add(aClass); 195 } 196 } 197 198 return answer.toArray(new PsiClass[answer.size()]); 199 } 200} 201