blob: 7fbde2d02ee4c601132686d9e21084dac1db0b3f [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.ide.util;
import com.intellij.ide.IdeBundle;
import com.intellij.openapi.application.ApplicationManager;
import com.intellij.openapi.diagnostic.Logger;
import com.intellij.openapi.module.Module;
import com.intellij.openapi.project.Project;
import com.intellij.openapi.project.ProjectBundle;
import com.intellij.openapi.roots.ModuleRootManager;
import com.intellij.openapi.roots.ProjectFileIndex;
import com.intellij.openapi.roots.ProjectRootManager;
import com.intellij.openapi.roots.impl.DirectoryIndex;
import com.intellij.openapi.roots.ui.configuration.CommonContentEntriesEditor;
import com.intellij.openapi.roots.ui.configuration.ProjectSettingsService;
import com.intellij.openapi.ui.Messages;
import com.intellij.openapi.util.Condition;
import com.intellij.openapi.vfs.VirtualFile;
import com.intellij.psi.PsiDirectory;
import com.intellij.psi.PsiElement;
import com.intellij.psi.PsiFile;
import com.intellij.psi.PsiManager;
import com.intellij.psi.impl.source.resolve.FileContextUtil;
import com.intellij.psi.search.GlobalSearchScope;
import com.intellij.psi.search.GlobalSearchScopes;
import com.intellij.util.*;
import com.intellij.util.containers.ContainerUtil;
import org.jetbrains.annotations.NotNull;
import org.jetbrains.annotations.Nullable;
import java.io.File;
import java.io.IOException;
import java.util.List;
// TODO this should eventually replace PackageUtil from java-impl
public class PlatformPackageUtil {
private static final Logger LOG = Logger.getInstance("com.intellij.ide.util.PlatformPackageUtil");
@Nullable
private static String findLongestExistingPackage(Project project, String packageName, GlobalSearchScope scope) {
final PsiManager manager = PsiManager.getInstance(project);
DirectoryIndex index = DirectoryIndex.getInstance(project);
String nameToMatch = packageName;
while (true) {
Query<VirtualFile> vFiles = index.getDirectoriesByPackageName(nameToMatch, false);
PsiDirectory directory = getWritableModuleDirectory(vFiles, scope, manager);
if (directory != null) return index.getPackageName(directory.getVirtualFile());
int lastDotIndex = nameToMatch.lastIndexOf('.');
if (lastDotIndex >= 0) {
nameToMatch = nameToMatch.substring(0, lastDotIndex);
}
else {
return null;
}
}
}
@Nullable
private static PsiDirectory getWritableModuleDirectory(@NotNull Query<VirtualFile> vFiles,
GlobalSearchScope scope,
PsiManager manager) {
for (VirtualFile vFile : vFiles) {
if (!scope.contains(vFile)) continue;
PsiDirectory directory = manager.findDirectory(vFile);
if (directory != null && directory.isValid() && directory.isWritable()) {
return directory;
}
}
return null;
}
@Nullable
public static PsiDirectory findOrCreateDirectoryForPackage(@NotNull final Project project,
@Nullable Module module,
GlobalSearchScope scope,
String packageName,
PsiDirectory baseDir,
boolean askUserToCreate,
ThreeState chooseFlag) throws IncorrectOperationException {
PsiDirectory psiDirectory = null;
if (chooseFlag == ThreeState.UNSURE && !"".equals(packageName)) {
String rootPackage = findLongestExistingPackage(project, packageName, scope);
if (rootPackage != null) {
int beginIndex = rootPackage.length() + 1;
packageName = beginIndex < packageName.length() ? packageName.substring(beginIndex) : "";
String postfixToShow = packageName.replace('.', File.separatorChar);
if (packageName.length() > 0) {
postfixToShow = File.separatorChar + postfixToShow;
}
psiDirectory =
DirectoryChooserUtil.selectDirectory(project, getPackageDirectories(project, rootPackage, scope), baseDir, postfixToShow);
if (psiDirectory == null) return null;
}
}
if (psiDirectory == null) {
if (chooseFlag == ThreeState.NO && baseDir != null) {
VirtualFile sourceRoot = ProjectRootManager.getInstance(project).getFileIndex().getSourceRootForFile(baseDir.getVirtualFile());
psiDirectory = PsiManager.getInstance(project).findDirectory(sourceRoot);
}
else {
if (module != null && !checkSourceRootsConfigured(module)) return null;
final GlobalSearchScope scope_ = scope;
List<PsiDirectory> dirs =
ContainerUtil
.mapNotNull(ProjectRootManager.getInstance(project).getContentSourceRoots(), new Function<VirtualFile, PsiDirectory>() {
@Override
public PsiDirectory fun(VirtualFile virtualFile) {
return scope_.contains(virtualFile) ? PsiManager.getInstance(project).findDirectory(virtualFile) : null;
}
});
psiDirectory = DirectoryChooserUtil.selectDirectory(project, dirs.toArray(new PsiDirectory[dirs.size()]), baseDir,
File.separatorChar + packageName.replace('.', File.separatorChar));
if (psiDirectory == null) return null;
final VirtualFile sourceRoot = ProjectRootManager.getInstance(project).getFileIndex().getSourceRootForFile(psiDirectory.getVirtualFile());
psiDirectory = PsiManager.getInstance(project).findDirectory(sourceRoot);
}
}
String restOfName = packageName;
boolean askedToCreate = false;
while (restOfName.length() > 0) {
final String name = getLeftPart(restOfName);
PsiDirectory foundExistingDirectory = psiDirectory.findSubdirectory(name);
if (foundExistingDirectory == null) {
if (!askedToCreate && askUserToCreate) {
if (!ApplicationManager.getApplication().isUnitTestMode()) {
int toCreate = Messages.showYesNoDialog(project,
IdeBundle.message("prompt.create.non.existing.package", packageName),
IdeBundle.message("title.package.not.found"),
Messages.getQuestionIcon());
if (toCreate != Messages.YES) {
return null;
}
}
askedToCreate = true;
}
final PsiDirectory psiDirectory_ = psiDirectory;
try {
psiDirectory = ActionRunner.runInsideWriteAction(new ActionRunner.InterruptibleRunnableWithResult<PsiDirectory>() {
@Override
public PsiDirectory run() throws Exception {
return psiDirectory_.createSubdirectory(name);
}
});
}
catch (IncorrectOperationException e) {
throw e;
}
catch (IOException e) {
throw new IncorrectOperationException(e);
}
catch (Exception e) {
LOG.error(e);
}
}
else {
psiDirectory = foundExistingDirectory;
}
restOfName = cutLeftPart(restOfName);
}
return psiDirectory;
}
public static GlobalSearchScope adjustScope(PsiDirectory baseDir,
GlobalSearchScope scope,
boolean skipSourceDirsForBaseTestDirectory,
boolean skipTestDirsForBaseSourceDirectory) {
if (baseDir != null) {
final ProjectFileIndex fileIndex = ProjectRootManager.getInstance(baseDir.getProject()).getFileIndex();
if (fileIndex.isInTestSourceContent(baseDir.getVirtualFile())) {
if (skipSourceDirsForBaseTestDirectory) {
return scope.intersectWith(GlobalSearchScopes.projectTestScope(baseDir.getProject()));
}
}
else {
if (skipTestDirsForBaseSourceDirectory) {
return scope.intersectWith(GlobalSearchScopes.projectProductionScope(baseDir.getProject()));
}
}
}
return scope;
}
private static PsiDirectory[] getPackageDirectories(Project project, String rootPackage, final GlobalSearchScope scope) {
final PsiManager manager = PsiManager.getInstance(project);
Query<VirtualFile> query = DirectoryIndex.getInstance(scope.getProject()).getDirectoriesByPackageName(rootPackage, true);
query = new FilteredQuery<VirtualFile>(query, new Condition<VirtualFile>() {
@Override
public boolean value(VirtualFile virtualFile) {
return scope.contains(virtualFile);
}
});
List<PsiDirectory> directories = ContainerUtil.mapNotNull(query.findAll(), new Function<VirtualFile, PsiDirectory>() {
@Override
public PsiDirectory fun(VirtualFile virtualFile) {
return manager.findDirectory(virtualFile);
}
});
return directories.toArray(new PsiDirectory[directories.size()]);
}
private static boolean checkSourceRootsConfigured(final Module module) {
VirtualFile[] sourceRoots = ModuleRootManager.getInstance(module).getSourceRoots();
if (sourceRoots.length == 0) {
Messages.showErrorDialog(
module.getProject(),
ProjectBundle.message("module.source.roots.not.configured.error", module.getName()),
ProjectBundle.message("module.source.roots.not.configured.title")
);
ProjectSettingsService
.getInstance(module.getProject()).showModuleConfigurationDialog(module.getName(), CommonContentEntriesEditor.NAME);
sourceRoots = ModuleRootManager.getInstance(module).getSourceRoots();
if (sourceRoots.length == 0) {
return false;
}
}
return true;
}
private static String getLeftPart(String packageName) {
int index = packageName.indexOf('.');
return index > -1 ? packageName.substring(0, index) : packageName;
}
private static String cutLeftPart(String packageName) {
int index = packageName.indexOf('.');
return index > -1 ? packageName.substring(index + 1) : "";
}
@Nullable
public static PsiDirectory findPossiblePackageDirectoryInModule(Module module, GlobalSearchScope scope, String packageName) {
if (!"".equals(packageName)) {
String rootPackage = findLongestExistingPackage(module.getProject(), packageName, scope);
if (rootPackage != null) {
final PsiDirectory[] psiDirectories = getPackageDirectories(module.getProject(), rootPackage, scope);
if (psiDirectories.length > 0) {
return psiDirectories[0];
}
}
}
if (!checkSourceRootsConfigured(module)) return null;
final VirtualFile[] sourceRoots = ModuleRootManager.getInstance(module).getSourceRoots();
for (VirtualFile sourceRoot : sourceRoots) {
final PsiDirectory directory = PsiManager.getInstance(module.getProject()).findDirectory(sourceRoot);
if (directory != null) {
return directory;
}
}
return null;
}
@Nullable
public static PsiDirectory getDirectory(@Nullable PsiElement element) {
if (element == null) return null;
// handle injection and fragment editor
PsiFile file = FileContextUtil.getContextFile(element);
return file == null ? null : file.getContainingDirectory();
}
}