| /* |
| * Copyright 2000-2014 JetBrains s.r.o. |
| * |
| * Licensed under the Apache License, Version 2.0 (the "License"); |
| * you may not use this file except in compliance with the License. |
| * You may obtain a copy of the License at |
| * |
| * http://www.apache.org/licenses/LICENSE-2.0 |
| * |
| * Unless required by applicable law or agreed to in writing, software |
| * distributed under the License is distributed on an "AS IS" BASIS, |
| * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. |
| * See the License for the specific language governing permissions and |
| * limitations under the License. |
| */ |
| package com.intellij.core; |
| |
| import com.intellij.openapi.diagnostic.Logger; |
| import com.intellij.openapi.util.io.FileUtil; |
| import com.intellij.openapi.vfs.VfsUtilCore; |
| import com.intellij.openapi.vfs.VirtualFile; |
| import com.intellij.psi.*; |
| import com.intellij.psi.impl.file.PsiPackageImpl; |
| import com.intellij.psi.impl.file.impl.JavaFileManager; |
| import com.intellij.psi.search.GlobalSearchScope; |
| import org.jetbrains.annotations.NotNull; |
| import org.jetbrains.annotations.Nullable; |
| |
| import java.util.*; |
| |
| /** |
| * @author yole |
| */ |
| public class CoreJavaFileManager implements JavaFileManager { |
| private static final Logger LOG = Logger.getInstance("#com.intellij.core.CoreJavaFileManager"); |
| |
| private final List<VirtualFile> myClasspath = new ArrayList<VirtualFile>(); |
| |
| private final PsiManager myPsiManager; |
| |
| public CoreJavaFileManager(PsiManager psiManager) { |
| myPsiManager = psiManager; |
| } |
| |
| private List<VirtualFile> roots() { |
| return myClasspath; |
| } |
| |
| @Override |
| public PsiPackage findPackage(@NotNull String packageName) { |
| final List<VirtualFile> files = findDirectoriesByPackageName(packageName); |
| if (!files.isEmpty()) { |
| return new PsiPackageImpl(myPsiManager, packageName); |
| } |
| return null; |
| } |
| |
| private List<VirtualFile> findDirectoriesByPackageName(String packageName) { |
| List<VirtualFile> result = new ArrayList<VirtualFile>(); |
| String dirName = packageName.replace(".", "/"); |
| for (VirtualFile root : roots()) { |
| VirtualFile classDir = root.findFileByRelativePath(dirName); |
| if (classDir != null) { |
| result.add(classDir); |
| } |
| } |
| return result; |
| } |
| |
| @Nullable |
| public PsiPackage getPackage(PsiDirectory dir) { |
| final VirtualFile file = dir.getVirtualFile(); |
| for (VirtualFile root : myClasspath) { |
| if (VfsUtilCore.isAncestor(root, file, false)) { |
| String relativePath = FileUtil.getRelativePath(root.getPath(), file.getPath(), '/'); |
| if (relativePath == null) continue; |
| return new PsiPackageImpl(myPsiManager, relativePath.replace('/', '.')); |
| } |
| } |
| return null; |
| } |
| |
| @Override |
| public PsiClass findClass(@NotNull String qName, @NotNull GlobalSearchScope scope) { |
| for (VirtualFile root : roots()) { |
| final PsiClass psiClass = findClassInClasspathRoot(qName, root, myPsiManager); |
| if (psiClass != null) { |
| return psiClass; |
| } |
| } |
| return null; |
| } |
| |
| @Nullable |
| public static PsiClass findClassInClasspathRoot(String qName, VirtualFile root, PsiManager psiManager) { |
| String pathRest = qName; |
| VirtualFile cur = root; |
| |
| while (true) { |
| int dot = pathRest.indexOf('.'); |
| if (dot < 0) break; |
| |
| String pathComponent = pathRest.substring(0, dot); |
| VirtualFile child = cur.findChild(pathComponent); |
| |
| if (child == null) break; |
| pathRest = pathRest.substring(dot + 1); |
| cur = child; |
| } |
| |
| String className = pathRest.replace('.', '$'); |
| int bucks = className.indexOf('$'); |
| |
| String rootClassName; |
| if (bucks < 0) { |
| rootClassName = className; |
| } |
| else { |
| rootClassName = className.substring(0, bucks); |
| className = className.substring(bucks + 1); |
| } |
| |
| VirtualFile vFile = cur.findChild(rootClassName + ".class"); |
| if (vFile == null) vFile = cur.findChild(rootClassName + ".java"); |
| |
| if (vFile != null) { |
| if (!vFile.isValid()) { |
| LOG.error("Invalid child of valid parent: " + vFile.getPath() + "; " + root.isValid() + " path=" + root.getPath()); |
| return null; |
| } |
| |
| final PsiFile file = psiManager.findFile(vFile); |
| if (file instanceof PsiClassOwner) { |
| final PsiClass[] classes = ((PsiClassOwner)file).getClasses(); |
| if (classes.length == 1) { |
| PsiClass curClass = classes[0]; |
| |
| if (bucks > 0) { |
| Stack<ClassAndOffsets> currentPath = new Stack<ClassAndOffsets>(); |
| currentPath.add(new ClassAndOffsets(curClass, 0, 0)); |
| currentPath.add(currentPath.peek()); |
| |
| while (currentPath.size() > 1) { |
| ClassAndOffsets classAndOffset = currentPath.pop(); |
| int newComponentStart = classAndOffset.componentStart; |
| int lookupStart = classAndOffset.lookupStart; |
| curClass = currentPath.peek().clazz; //owner class |
| |
| while (lookupStart <= className.length()) { |
| int bucksIndex = className.indexOf("$", lookupStart); |
| bucksIndex = bucksIndex < 0 ? className.length(): bucksIndex; |
| |
| String component = className.substring(newComponentStart, bucksIndex); |
| PsiClass inner = curClass.findInnerClassByName(component, false); |
| |
| lookupStart = bucksIndex + 1; |
| if (inner == null) { |
| continue; |
| } |
| |
| currentPath.add(new ClassAndOffsets(inner, newComponentStart, lookupStart)); |
| |
| newComponentStart = lookupStart; |
| curClass = inner; |
| } |
| |
| if (lookupStart == newComponentStart) { |
| return curClass; |
| } |
| } |
| |
| return null; |
| |
| } else { |
| return curClass; |
| } |
| } |
| } |
| } |
| |
| return null; |
| } |
| |
| @NotNull |
| @Override |
| public PsiClass[] findClasses(@NotNull String qName, @NotNull GlobalSearchScope scope) { |
| List<PsiClass> result = new ArrayList<PsiClass>(); |
| for (VirtualFile file : roots()) { |
| final PsiClass psiClass = findClassInClasspathRoot(qName, file, myPsiManager); |
| if (psiClass != null) { |
| result.add(psiClass); |
| } |
| } |
| return result.toArray(new PsiClass[result.size()]); |
| } |
| |
| @NotNull |
| @Override |
| public Collection<String> getNonTrivialPackagePrefixes() { |
| return Collections.emptyList(); |
| } |
| |
| public void addToClasspath(VirtualFile root) { |
| myClasspath.add(root); |
| } |
| |
| private static class ClassAndOffsets { |
| |
| final PsiClass clazz; |
| final int componentStart; |
| final int lookupStart; |
| |
| ClassAndOffsets(PsiClass clazz, int componentStart, int lookupStart) { |
| this.clazz = clazz; |
| this.componentStart = componentStart; |
| this.lookupStart = lookupStart; |
| } |
| } |
| } |