blob: 0530f34e653acf77097734a81d27d2682e8f8c48 [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.command.CommandProcessor;
import com.intellij.openapi.diagnostic.Logger;
import com.intellij.openapi.module.Module;
import com.intellij.openapi.module.ModuleUtil;
import com.intellij.openapi.project.Project;
import com.intellij.openapi.project.ProjectBundle;
import com.intellij.openapi.projectRoots.impl.ProjectRootUtil;
import com.intellij.openapi.roots.*;
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.Computable;
import com.intellij.openapi.util.text.StringUtil;
import com.intellij.openapi.vfs.VirtualFile;
import com.intellij.psi.*;
import com.intellij.psi.search.GlobalSearchScope;
import com.intellij.util.ActionRunner;
import com.intellij.util.IncorrectOperationException;
import com.intellij.util.Query;
import org.jetbrains.annotations.NotNull;
import org.jetbrains.annotations.Nullable;
import org.jetbrains.jps.model.java.JavaModuleSourceRootTypes;
import java.io.File;
import java.io.IOException;
import java.util.ArrayList;
import java.util.List;
public class PackageUtil {
private static final Logger LOG = Logger.getInstance("com.intellij.ide.util.PackageUtil");
@Nullable
public static PsiDirectory findPossiblePackageDirectoryInModule(Module module, String packageName) {
return findPossiblePackageDirectoryInModule(module, packageName, true);
}
@Nullable
public static PsiDirectory findPossiblePackageDirectoryInModule(Module module, String packageName, boolean preferNonGeneratedRoots) {
final Project project = module.getProject();
PsiDirectory psiDirectory = null;
if (!StringUtil.isEmptyOrSpaces(packageName)) {
PsiPackage rootPackage = findLongestExistingPackage(project, packageName);
if (rootPackage != null) {
final PsiDirectory[] psiDirectories = getPackageDirectoriesInModule(rootPackage, module);
if (psiDirectories.length > 0) {
psiDirectory = psiDirectories[0];
// If we prefer to find a non-generated PsiDirectory for the given package name, search through all
// the directories for the first dir not marked as generated and use that one instead
if (preferNonGeneratedRoots && psiDirectories.length > 1) {
for (PsiDirectory dir : psiDirectories) {
if (!GeneratedSourcesFilter.isGeneratedSourceByAnyFilter(dir.getVirtualFile(), project)) {
psiDirectory = dir;
break;
}
}
}
}
}
}
if (psiDirectory == null) {
if (checkSourceRootsConfigured(module)) {
final List<VirtualFile> sourceRoots = ModuleRootManager.getInstance(module).getSourceRoots(JavaModuleSourceRootTypes.SOURCES);
for (VirtualFile sourceRoot : sourceRoots) {
final PsiDirectory directory = PsiManager.getInstance(project).findDirectory(sourceRoot);
if (directory != null) {
psiDirectory = directory;
break;
}
}
}
}
return psiDirectory;
}
/**
* @deprecated
*/
@Nullable
public static PsiDirectory findOrCreateDirectoryForPackage(Project project,
String packageName,
PsiDirectory baseDir,
boolean askUserToCreate) throws IncorrectOperationException {
return findOrCreateDirectoryForPackage(project, packageName, baseDir, askUserToCreate, false);
}
/**
* @deprecated
*/
@Nullable
public static PsiDirectory findOrCreateDirectoryForPackage(Project project,
String packageName,
PsiDirectory baseDir,
boolean askUserToCreate, boolean filterSourceDirsForTestBaseDir) throws IncorrectOperationException {
PsiDirectory psiDirectory = null;
if (!"".equals(packageName)) {
PsiPackage rootPackage = findLongestExistingPackage(project, packageName);
if (rootPackage != null) {
int beginIndex = rootPackage.getQualifiedName().length() + 1;
packageName = beginIndex < packageName.length() ? packageName.substring(beginIndex) : "";
String postfixToShow = packageName.replace('.', File.separatorChar);
if (packageName.length() > 0) {
postfixToShow = File.separatorChar + postfixToShow;
}
PsiDirectory[] directories = rootPackage.getDirectories();
if (filterSourceDirsForTestBaseDir) {
directories = filterSourceDirectories(baseDir, project, directories);
}
psiDirectory = DirectoryChooserUtil.selectDirectory(project, directories, baseDir, postfixToShow);
if (psiDirectory == null) return null;
}
}
if (psiDirectory == null) {
PsiDirectory[] sourceDirectories = ProjectRootUtil.getSourceRootDirectories(project);
psiDirectory = DirectoryChooserUtil.selectDirectory(project, sourceDirectories, baseDir,
File.separatorChar + packageName.replace('.', File.separatorChar));
if (psiDirectory == null) return null;
}
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) {
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;
}
psiDirectory = createSubdirectory(psiDirectory, name, project);
}
else {
psiDirectory = foundExistingDirectory;
}
restOfName = cutLeftPart(restOfName);
}
return psiDirectory;
}
private static PsiDirectory createSubdirectory(final PsiDirectory oldDirectory,
final String name, Project project) throws IncorrectOperationException {
final PsiDirectory[] psiDirectory = new PsiDirectory[1];
final IncorrectOperationException[] exception = new IncorrectOperationException[1];
CommandProcessor.getInstance().executeCommand(project, new Runnable(){
public void run() {
psiDirectory[0] = ApplicationManager.getApplication().runWriteAction(new Computable<PsiDirectory>() {
public PsiDirectory compute() {
try {
return oldDirectory.createSubdirectory(name);
}
catch (IncorrectOperationException e) {
exception[0] = e;
return null;
}
}
});
}
}, IdeBundle.message("command.create.new.subdirectory"), null);
if (exception[0] != null) throw exception[0];
return psiDirectory[0];
}
@Nullable
public static PsiDirectory findOrCreateDirectoryForPackage(@NotNull Module module,
String packageName,
@Nullable PsiDirectory baseDir,
boolean askUserToCreate) throws IncorrectOperationException {
return findOrCreateDirectoryForPackage(module, packageName, baseDir, askUserToCreate, false);
}
@Nullable
public static PsiDirectory findOrCreateDirectoryForPackage(@NotNull Module module,
String packageName,
PsiDirectory baseDir,
boolean askUserToCreate,
boolean filterSourceDirsForBaseTestDirectory) throws IncorrectOperationException {
final Project project = module.getProject();
PsiDirectory psiDirectory = null;
if (!packageName.isEmpty()) {
PsiPackage rootPackage = findLongestExistingPackage(module, packageName);
if (rootPackage != null) {
int beginIndex = rootPackage.getQualifiedName().length() + 1;
packageName = beginIndex < packageName.length() ? packageName.substring(beginIndex) : "";
String postfixToShow = packageName.replace('.', File.separatorChar);
if (packageName.length() > 0) {
postfixToShow = File.separatorChar + postfixToShow;
}
PsiDirectory[] moduleDirectories = getPackageDirectoriesInModule(rootPackage, module);
if (filterSourceDirsForBaseTestDirectory) {
moduleDirectories = filterSourceDirectories(baseDir, project, moduleDirectories);
}
psiDirectory = DirectoryChooserUtil.selectDirectory(project, moduleDirectories, baseDir, postfixToShow);
if (psiDirectory == null) return null;
}
}
if (psiDirectory == null) {
if (!checkSourceRootsConfigured(module, askUserToCreate)) return null;
final List<VirtualFile> sourceRoots = ModuleRootManager.getInstance(module).getSourceRoots(JavaModuleSourceRootTypes.SOURCES);
List<PsiDirectory> directoryList = new ArrayList<PsiDirectory>();
for (VirtualFile sourceRoot : sourceRoots) {
final PsiDirectory directory = PsiManager.getInstance(project).findDirectory(sourceRoot);
if (directory != null) {
directoryList.add(directory);
}
}
PsiDirectory[] sourceDirectories = directoryList.toArray(new PsiDirectory[directoryList.size()]);
psiDirectory = DirectoryChooserUtil.selectDirectory(project, sourceDirectories, baseDir,
File.separatorChar + packageName.replace('.', File.separatorChar));
if (psiDirectory == null) return null;
}
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 psiDirectory1 = psiDirectory;
try {
psiDirectory = ActionRunner.runInsideWriteAction(new ActionRunner.InterruptibleRunnableWithResult<PsiDirectory>() {
public PsiDirectory run() throws Exception {
return psiDirectory1.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;
}
private static PsiDirectory[] filterSourceDirectories(PsiDirectory baseDir, Project project, PsiDirectory[] moduleDirectories) {
final ProjectFileIndex fileIndex = ProjectRootManager.getInstance(project).getFileIndex();
if (fileIndex.isInTestSourceContent(baseDir.getVirtualFile())) {
List<PsiDirectory> result = new ArrayList<PsiDirectory>();
for (PsiDirectory moduleDirectory : moduleDirectories) {
if (fileIndex.isInTestSourceContent(moduleDirectory.getVirtualFile())) {
result.add(moduleDirectory);
}
}
moduleDirectories = result.toArray(new PsiDirectory[result.size()]);
}
return moduleDirectories;
}
private static PsiDirectory[] getPackageDirectoriesInModule(PsiPackage rootPackage, Module module) {
return rootPackage.getDirectories(GlobalSearchScope.moduleScope(module));
}
private static PsiPackage findLongestExistingPackage(Project project, String packageName) {
PsiManager manager = PsiManager.getInstance(project);
String nameToMatch = packageName;
while (true) {
PsiPackage aPackage = JavaPsiFacade.getInstance(manager.getProject()).findPackage(nameToMatch);
if (aPackage != null && isWritablePackage(aPackage)) return aPackage;
int lastDotIndex = nameToMatch.lastIndexOf('.');
if (lastDotIndex >= 0) {
nameToMatch = nameToMatch.substring(0, lastDotIndex);
}
else {
return null;
}
}
}
private static boolean isWritablePackage(PsiPackage aPackage) {
PsiDirectory[] directories = aPackage.getDirectories();
for (PsiDirectory directory : directories) {
if (directory.isValid() && directory.isWritable()) {
return true;
}
}
return false;
}
private static PsiDirectory getWritableModuleDirectory(@NotNull Query<VirtualFile> vFiles, @NotNull Module module, PsiManager manager) {
for (VirtualFile vFile : vFiles) {
if (ModuleUtil.findModuleForFile(vFile, module.getProject()) != module) continue;
PsiDirectory directory = manager.findDirectory(vFile);
if (directory != null && directory.isValid() && directory.isWritable()) {
return directory;
}
}
return null;
}
private static PsiPackage findLongestExistingPackage(Module module, String packageName) {
final PsiManager manager = PsiManager.getInstance(module.getProject());
String nameToMatch = packageName;
while (true) {
Query<VirtualFile> vFiles = ModulePackageIndex.getInstance(module).getDirsByPackageName(nameToMatch, false);
PsiDirectory directory = getWritableModuleDirectory(vFiles, module, manager);
if (directory != null) return JavaDirectoryService.getInstance().getPackage(directory);
int lastDotIndex = nameToMatch.lastIndexOf('.');
if (lastDotIndex >= 0) {
nameToMatch = nameToMatch.substring(0, lastDotIndex);
}
else {
return null;
}
}
}
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) : "";
}
public static boolean checkSourceRootsConfigured(final Module module) {
return checkSourceRootsConfigured(module, true);
}
public static boolean checkSourceRootsConfigured(final Module module, final boolean askUserToSetupSourceRoots) {
List<VirtualFile> sourceRoots = ModuleRootManager.getInstance(module).getSourceRoots(JavaModuleSourceRootTypes.SOURCES);
if (sourceRoots.isEmpty()) {
if (!askUserToSetupSourceRoots) {
return false;
}
Project project = module.getProject();
Messages.showErrorDialog(project,
ProjectBundle.message("module.source.roots.not.configured.error", module.getName()),
ProjectBundle.message("module.source.roots.not.configured.title"));
ProjectSettingsService.getInstance(project).showModuleConfigurationDialog(module.getName(), CommonContentEntriesEditor.NAME);
sourceRoots = ModuleRootManager.getInstance(module).getSourceRoots(JavaModuleSourceRootTypes.SOURCES);
if (sourceRoots.isEmpty()) {
return false;
}
}
return true;
}
@NotNull
public static PsiDirectory findOrCreateSubdirectory(@NotNull PsiDirectory directory, @NotNull String directoryName) {
PsiDirectory subDirectory = directory.findSubdirectory(directoryName);
if (subDirectory == null) {
subDirectory = directory.createSubdirectory(directoryName);
}
return subDirectory;
}
}