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 }