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.lang.resolve.java; 018 019 import com.google.common.collect.Lists; 020 import com.google.common.collect.Sets; 021 import com.intellij.openapi.application.ApplicationManager; 022 import com.intellij.openapi.diagnostic.Logger; 023 import com.intellij.openapi.project.Project; 024 import com.intellij.openapi.util.Comparing; 025 import com.intellij.openapi.vfs.VirtualFile; 026 import com.intellij.psi.PsiAnnotation; 027 import com.intellij.psi.PsiClass; 028 import com.intellij.psi.PsiPackage; 029 import com.intellij.psi.search.DelegatingGlobalSearchScope; 030 import com.intellij.psi.search.GlobalSearchScope; 031 import org.jetbrains.annotations.NotNull; 032 import org.jetbrains.annotations.Nullable; 033 import org.jetbrains.jet.lang.resolve.java.resolver.JavaAnnotationResolver; 034 import org.jetbrains.jet.lang.resolve.name.FqName; 035 import org.jetbrains.jet.lang.types.lang.KotlinBuiltIns; 036 import org.jetbrains.jet.plugin.JetFileType; 037 038 import javax.annotation.PostConstruct; 039 import javax.inject.Inject; 040 import java.util.List; 041 import java.util.Set; 042 043 public class PsiClassFinderImpl implements PsiClassFinder { 044 private static final Logger LOG = Logger.getInstance(PsiClassFinderImpl.class); 045 046 @NotNull 047 private Project project; 048 049 private GlobalSearchScope javaSearchScope; 050 private JavaPsiFacadeKotlinHacks javaFacade; 051 052 @Inject 053 public void setProject(@NotNull Project project) { 054 this.project = project; 055 } 056 057 @PostConstruct 058 public void initialize() { 059 javaSearchScope = new DelegatingGlobalSearchScope(GlobalSearchScope.allScope(project)) { 060 @Override 061 public boolean contains(VirtualFile file) { 062 return myBaseScope.contains(file) && file.getFileType() != JetFileType.INSTANCE; 063 } 064 065 @Override 066 public int compare(VirtualFile file1, VirtualFile file2) { 067 // TODO: this is a hackish workaround for the following problem: 068 // since we are working with the allScope(), if the same class FqName 069 // to be on the class path twice, because it is included into different libraries 070 // (e.g. junit-4.0.jar is used as a separate library and as a part of idea_full) 071 // the two libraries are attached to different modules, the parent compare() 072 // can't tell which one comes first, so they can come in random order 073 // To fix this, we sort additionally by the full path, to make the ordering deterministic 074 // TODO: Delete this hack when proper scopes are used 075 int compare = super.compare(file1, file2); 076 if (compare == 0) { 077 return Comparing.compare(file1.getPath(), file2.getPath()); 078 } 079 return compare; 080 } 081 }; 082 javaFacade = new JavaPsiFacadeKotlinHacks(project); 083 } 084 085 086 @Override 087 @Nullable 088 public PsiClass findPsiClass(@NotNull FqName qualifiedName, @NotNull RuntimeClassesHandleMode runtimeClassesHandleMode) { 089 PsiClass original = javaFacade.findClass(qualifiedName.asString(), javaSearchScope); 090 091 if (original != null) { 092 String classQualifiedName = original.getQualifiedName(); 093 FqName actualQualifiedName = classQualifiedName != null ? new FqName(classQualifiedName) : null; 094 if (!qualifiedName.equals(actualQualifiedName)) { 095 throw new IllegalStateException("requested " + qualifiedName + ", got " + actualQualifiedName); 096 } 097 } 098 099 if (original instanceof JetJavaMirrorMarker) { 100 throw new IllegalStateException("JetJavaMirrorMaker is not possible in resolve.java, resolving: " + qualifiedName); 101 } 102 103 if (original == null) { 104 return null; 105 } 106 107 if (KotlinBuiltIns.BUILT_INS_PACKAGE_FQ_NAME.equals(qualifiedName.parent())) { 108 PsiAnnotation assertInvisibleAnnotation = JavaAnnotationResolver.findOwnAnnotation( 109 original, JvmStdlibNames.ASSERT_INVISIBLE_IN_RESOLVER.getFqName().asString()); 110 111 if (assertInvisibleAnnotation != null) { 112 switch (runtimeClassesHandleMode) { 113 case IGNORE: 114 break; 115 case REPORT_ERROR: 116 if (ApplicationManager.getApplication().isInternal()) { 117 LOG.error("classpath is configured incorrectly:" + 118 " class " + qualifiedName + " from runtime must not be loaded by compiler"); 119 } 120 break; 121 default: 122 throw new IllegalStateException("unknown parameter value: " + runtimeClassesHandleMode); 123 } 124 return null; 125 } 126 } 127 128 return original; 129 } 130 131 @Override 132 @Nullable 133 public PsiPackage findPsiPackage(@NotNull FqName qualifiedName) { 134 return javaFacade.findPackage(qualifiedName.asString()); 135 } 136 137 @NotNull 138 @Override 139 public List<PsiClass> findPsiClasses(@NotNull PsiPackage psiPackage) { 140 return filterDuplicateClasses(psiPackage.getClasses()); 141 } 142 143 @NotNull 144 @Override 145 public List<PsiClass> findInnerPsiClasses(@NotNull PsiClass psiClass) { 146 return filterDuplicateClasses(psiClass.getInnerClasses()); 147 } 148 149 private static List<PsiClass> filterDuplicateClasses(PsiClass[] classes) { 150 Set<String> addedQualifiedNames = Sets.newHashSet(); 151 List<PsiClass> filteredClasses = Lists.newArrayList(); 152 153 for (PsiClass aClass : classes) { 154 String qualifiedName = aClass.getQualifiedName(); 155 156 if (qualifiedName != null) { 157 if (addedQualifiedNames.add(qualifiedName)) { 158 filteredClasses.add(aClass); 159 } 160 } 161 } 162 163 return filteredClasses; 164 } 165 }