blob: 0cfb60b3ae6929a59600f07489f17ccc7853e691 [file] [log] [blame]
/*
* 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;
}
}
}