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.intellij.core.CoreJavaFileManager;
021    import com.intellij.openapi.progress.ProgressIndicatorProvider;
022    import com.intellij.openapi.project.Project;
023    import com.intellij.psi.*;
024    import com.intellij.psi.impl.JavaPsiFacadeImpl;
025    import com.intellij.psi.impl.file.impl.JavaFileManager;
026    import com.intellij.psi.search.GlobalSearchScope;
027    import com.intellij.util.containers.ContainerUtil;
028    import com.intellij.util.containers.HashMap;
029    import org.jetbrains.annotations.NotNull;
030    import org.jetbrains.annotations.Nullable;
031    
032    import java.util.LinkedHashSet;
033    import java.util.List;
034    import java.util.Map;
035    
036    /**
037     * TODO Temporary class until {@link JavaPsiFacadeImpl} hacked.
038     *
039     * @see JavaPsiFacadeImpl
040     */
041    public class JavaPsiFacadeKotlinHacks {
042        public interface KotlinFinderMarker {}
043    
044        private final JavaFileManager javaFileManager;
045        private final List<PsiElementFinder> extensionPsiElementFinders;
046    
047        public JavaPsiFacadeKotlinHacks(@NotNull Project project) {
048            this.javaFileManager = findJavaFileManager(project);
049            this.extensionPsiElementFinders = Lists.newArrayList();
050            for (PsiElementFinder finder : project.getExtensions(PsiElementFinder.EP_NAME)) {
051                if (!(finder instanceof KotlinFinderMarker)) {
052                    this.extensionPsiElementFinders.add(finder);
053                }
054            }
055        }
056    
057        @NotNull
058        private JavaFileManager findJavaFileManager(@NotNull Project project) {
059            JavaFileManager javaFileManager = project.getComponent(JavaFileManager.class);
060            if (javaFileManager != null) {
061                return javaFileManager;
062            }
063            javaFileManager = project.getComponent(CoreJavaFileManager.class);
064            if (javaFileManager != null) {
065                // TODO: why it is not found by JavaFileManager?
066                return javaFileManager;
067            }
068            throw new IllegalStateException("JavaFileManager component is not found in project");
069        }
070    
071        @Nullable
072        public PsiPackage findPackage(@NotNull String qualifiedName) {
073            PsiPackage psiPackage = javaFileManager.findPackage(qualifiedName);
074            if (psiPackage != null) {
075                return psiPackage;
076            }
077    
078            for (PsiElementFinder finder : extensionPsiElementFinders) {
079                psiPackage = finder.findPackage(qualifiedName);
080                if (psiPackage != null) {
081                    return psiPackage;
082                }
083            }
084            return psiPackage;
085        }
086    
087        public PsiClass findClass(@NotNull String qualifiedName, @NotNull GlobalSearchScope scope) {
088            ProgressIndicatorProvider.checkCanceled(); // We hope this method is being called often enough to cancel daemon processes smoothly
089    
090            PsiClass aClass = javaFileManager.findClass(qualifiedName, scope);
091            if (aClass != null) {
092                return aClass;
093            }
094    
095            for (PsiElementFinder finder : extensionPsiElementFinders) {
096                aClass = finder.findClass(qualifiedName, scope);
097                if (aClass != null) {
098                    return aClass;
099                }
100            }
101    
102            return null;
103        }
104    
105        public PsiPackage[] getSubPackages(@NotNull PsiPackage psiPackage) {
106            GlobalSearchScope scope = GlobalSearchScope.allScope(psiPackage.getProject());
107    
108            LinkedHashSet<PsiPackage> result = new LinkedHashSet<PsiPackage>();
109            for (PsiElementFinder finder : extensionPsiElementFinders) {
110                PsiPackage[] packages = finder.getSubPackages(psiPackage, scope);
111                ContainerUtil.addAll(result, packages);
112            }
113            ContainerUtil.addAll(result, getDefaultSubPackages(psiPackage, scope));
114    
115            return result.toArray(new PsiPackage[result.size()]);
116        }
117    
118        private static PsiPackage[] getDefaultSubPackages(@NotNull PsiPackage psiPackage, @NotNull GlobalSearchScope scope) {
119            Map<String, PsiPackage> packagesMap = new HashMap<String, PsiPackage>();
120            String qualifiedName = psiPackage.getQualifiedName();
121            for (PsiDirectory dir : psiPackage.getDirectories(scope)) {
122                PsiDirectory[] subDirs = dir.getSubdirectories();
123                for (PsiDirectory subDir : subDirs) {
124                    PsiPackage aPackage = JavaDirectoryService.getInstance().getPackage(subDir);
125                    if (aPackage != null) {
126                        String subQualifiedName = aPackage.getQualifiedName();
127                        if (subQualifiedName.startsWith(qualifiedName) && !packagesMap.containsKey(subQualifiedName)) {
128                            packagesMap.put(aPackage.getQualifiedName(), aPackage);
129                        }
130                    }
131                }
132            }
133    
134            packagesMap.remove(qualifiedName);    // avoid SOE caused by returning a package as a subpackage of itself
135            return packagesMap.values().toArray(new PsiPackage[packagesMap.size()]);
136        }
137    }