blob: d3c4858ffc3495082a801b69596bb694f66b186e [file] [log] [blame]
/*
* Copyright 2000-2009 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.codeInsight.daemon.quickFix;
import com.intellij.codeInsight.daemon.QuickFixBundle;
import com.intellij.codeInsight.daemon.impl.quickfix.CreateClassKind;
import com.intellij.codeInsight.daemon.impl.quickfix.CreateFromUsageUtils;
import com.intellij.codeInspection.LocalQuickFixAndIntentionActionOnPsiElement;
import com.intellij.ide.util.DirectoryChooserUtil;
import com.intellij.openapi.application.ApplicationManager;
import com.intellij.openapi.application.Result;
import com.intellij.openapi.command.WriteCommandAction;
import com.intellij.openapi.diagnostic.Logger;
import com.intellij.openapi.editor.Editor;
import com.intellij.openapi.module.Module;
import com.intellij.openapi.project.Project;
import com.intellij.openapi.roots.JavaProjectRootsUtil;
import com.intellij.openapi.roots.ProjectFileIndex;
import com.intellij.openapi.roots.ProjectRootManager;
import com.intellij.openapi.vfs.VirtualFile;
import com.intellij.psi.*;
import com.intellij.psi.search.GlobalSearchScope;
import com.intellij.psi.util.ClassKind;
import com.intellij.psi.util.CreateClassUtil;
import com.intellij.util.IncorrectOperationException;
import org.jetbrains.annotations.NotNull;
import org.jetbrains.annotations.Nullable;
import java.util.*;
/**
* @author peter
*/
public class CreateClassOrPackageFix extends LocalQuickFixAndIntentionActionOnPsiElement {
private static final Logger LOG = Logger.getInstance("#com.intellij.codeInsight.daemon.quickFix.CreateClassOrPackageFix");
private final List<PsiDirectory> myWritableDirectoryList;
private final String myPresentation;
@Nullable private final ClassKind myClassKind;
@Nullable private final String mySuperClass;
private final String myRedPart;
@Nullable private final String myTemplateName;
@Nullable
public static CreateClassOrPackageFix createFix(@NotNull final String qualifiedName,
@NotNull final GlobalSearchScope scope,
@NotNull final PsiElement context,
@Nullable final PsiPackage basePackage,
@Nullable ClassKind kind,
@Nullable String superClass,
@Nullable String templateName) {
final List<PsiDirectory> directories = getWritableDirectoryListDefault(basePackage, scope, context.getManager());
if (directories.isEmpty()) {
return null;
}
final String redPart = basePackage == null ? qualifiedName : qualifiedName.substring(basePackage.getQualifiedName().length() + 1);
final int dot = redPart.indexOf('.');
final boolean fixPath = dot >= 0;
final String firstRedName = fixPath ? redPart.substring(0, dot) : redPart;
for (Iterator<PsiDirectory> i = directories.iterator(); i.hasNext(); ) {
if (!checkCreateClassOrPackage(kind != null && !fixPath, i.next(), firstRedName)) {
i.remove();
}
}
return new CreateClassOrPackageFix(directories,
context,
fixPath ? qualifiedName : redPart,
redPart,
kind,
superClass,
templateName);
}
@Nullable
public static CreateClassOrPackageFix createFix(@NotNull final String qualifiedName,
@NotNull final PsiElement context,
@Nullable ClassKind kind,
String superClass) {
return createFix(qualifiedName, context.getResolveScope(), context, null, kind, superClass, null);
}
private CreateClassOrPackageFix(@NotNull List<PsiDirectory> writableDirectoryList,
@NotNull PsiElement context,
@NotNull String presentation,
@NotNull String redPart,
@Nullable ClassKind kind,
@Nullable String superClass,
@Nullable final String templateName) {
super(context);
myRedPart = redPart;
myTemplateName = templateName;
myWritableDirectoryList = writableDirectoryList;
myClassKind = kind;
mySuperClass = superClass;
myPresentation = presentation;
}
@Override
@NotNull
public String getText() {
return QuickFixBundle.message(
myClassKind == ClassKind.INTERFACE ? "create.interface.text" : myClassKind != null ? "create.class.text" : "create.package.text",
myPresentation);
}
@Override
@NotNull
public String getFamilyName() {
return getText();
}
@Override
public void invoke(@NotNull final Project project,
@NotNull final PsiFile file,
@Nullable("is null when called from inspection") Editor editor,
@NotNull final PsiElement startElement,
@NotNull PsiElement endElement) {
if (isAvailable(project, null, file)) {
new WriteCommandAction(project) {
@Override
protected void run(Result result) throws Throwable {
final PsiDirectory directory = chooseDirectory(project, file);
if (directory == null) return;
ApplicationManager.getApplication().runWriteAction(new Runnable() {
@Override
public void run() {
doCreate(directory, startElement);
}
});
}
}.execute();
}
}
private static boolean checkCreateClassOrPackage(final boolean createJavaClass, final PsiDirectory directory, final String name) {
try {
if (createJavaClass) {
JavaDirectoryService.getInstance().checkCreateClass(directory, name);
}
else {
directory.checkCreateSubdirectory(name);
}
return true;
}
catch (IncorrectOperationException ex) {
return false;
}
}
@Nullable
private PsiDirectory chooseDirectory(final Project project, final PsiFile file) {
PsiDirectory preferredDirectory = myWritableDirectoryList.isEmpty() ? null : myWritableDirectoryList.get(0);
final ProjectFileIndex fileIndex = ProjectRootManager.getInstance(project).getFileIndex();
final VirtualFile virtualFile = file.getVirtualFile();
assert virtualFile != null;
final Module moduleForFile = fileIndex.getModuleForFile(virtualFile);
if (myWritableDirectoryList.size() > 1 && !ApplicationManager.getApplication().isUnitTestMode()) {
if (moduleForFile != null) {
for (PsiDirectory directory : myWritableDirectoryList) {
if (fileIndex.getModuleForFile(directory.getVirtualFile()) == moduleForFile) {
preferredDirectory = directory;
break;
}
}
}
return DirectoryChooserUtil
.chooseDirectory(myWritableDirectoryList.toArray(new PsiDirectory[myWritableDirectoryList.size()]),
preferredDirectory, project,
new HashMap<PsiDirectory, String>());
}
return preferredDirectory;
}
private void doCreate(final PsiDirectory baseDirectory, PsiElement myContext) {
final PsiManager manager = baseDirectory.getManager();
PsiDirectory directory = baseDirectory;
String lastName;
for (StringTokenizer st = new StringTokenizer(myRedPart, "."); ;) {
lastName = st.nextToken();
if (st.hasMoreTokens()) {
try {
final PsiDirectory subdirectory = directory.findSubdirectory(lastName);
directory = subdirectory != null ? subdirectory : directory.createSubdirectory(lastName);
}
catch (IncorrectOperationException e) {
CreateFromUsageUtils.scheduleFileOrPackageCreationFailedMessageBox(e, lastName, directory, true);
return;
}
}
else {
break;
}
}
if (myClassKind != null) {
PsiClass createdClass;
if (myTemplateName != null) {
createdClass = CreateClassUtil.createClassFromCustomTemplate(directory, null, lastName, myTemplateName);
}
else {
createdClass = CreateFromUsageUtils
.createClass(myClassKind == ClassKind.INTERFACE ? CreateClassKind.INTERFACE : CreateClassKind.CLASS, directory, lastName,
manager, myContext, null, mySuperClass);
}
if (createdClass != null) {
createdClass.navigate(true);
}
}
else {
try {
directory.createSubdirectory(lastName);
}
catch (IncorrectOperationException e) {
CreateFromUsageUtils.scheduleFileOrPackageCreationFailedMessageBox(e, lastName, directory, true);
}
}
}
@Override
public boolean startInWriteAction() {
return false;
}
public static List<PsiDirectory> getWritableDirectoryListDefault(@Nullable final PsiPackage context,
final GlobalSearchScope scope,
final PsiManager psiManager) {
if (LOG.isDebugEnabled()) {
LOG.debug("Getting writable directory list for package '" + (context == null ? null : context.getQualifiedName()) + "', scope=" + scope);
}
final List<PsiDirectory> writableDirectoryList = new ArrayList<PsiDirectory>();
if (context != null) {
for (PsiDirectory directory : context.getDirectories()) {
if (LOG.isDebugEnabled()) {
LOG.debug("Package directory: " + directory);
}
VirtualFile virtualFile = directory.getVirtualFile();
if (directory.isWritable() && scope.contains(virtualFile)
&& !JavaProjectRootsUtil.isInGeneratedCode(virtualFile, psiManager.getProject())) {
writableDirectoryList.add(directory);
}
}
}
else {
for (VirtualFile root : JavaProjectRootsUtil.getSuitableDestinationSourceRoots(psiManager.getProject())) {
PsiDirectory directory = psiManager.findDirectory(root);
if (LOG.isDebugEnabled()) {
LOG.debug("Root: " + root + ", directory: " + directory);
}
if (directory != null && directory.isWritable() && scope.contains(directory.getVirtualFile())) {
writableDirectoryList.add(directory);
}
}
}
if (LOG.isDebugEnabled()) {
LOG.debug("Result " + writableDirectoryList);
}
return writableDirectoryList;
}
}