Java: disallow making builder class from the constructor class itself (IDEA-254858)

GitOrigin-RevId: 071a0fd3c45f2b28b73b811e0d844a0f1b911372
This commit is contained in:
Bas Leijdekkers
2024-09-03 17:08:26 +02:00
committed by intellij-monorepo-bot
parent cfc65a9890
commit 2870f49e0a
4 changed files with 68 additions and 71 deletions

View File

@@ -16,6 +16,7 @@ import com.intellij.openapi.project.Project;
import com.intellij.openapi.ui.InputValidatorEx;
import com.intellij.openapi.ui.Messages;
import com.intellij.openapi.ui.Splitter;
import com.intellij.openapi.util.NlsContexts;
import com.intellij.openapi.util.NlsSafe;
import com.intellij.openapi.util.text.StringUtil;
import com.intellij.openapi.wm.IdeFocusManager;
@@ -25,6 +26,7 @@ import com.intellij.psi.codeStyle.VariableKind;
import com.intellij.psi.search.GlobalSearchScope;
import com.intellij.psi.util.PropertyUtilBase;
import com.intellij.refactoring.HelpID;
import com.intellij.refactoring.MoveDestination;
import com.intellij.refactoring.PackageWrapper;
import com.intellij.refactoring.RefactoringBundle;
import com.intellij.refactoring.move.moveClassesOrPackages.DestinationFolderComboBox;
@@ -71,7 +73,6 @@ public class ReplaceConstructorWithBuilderDialog extends RefactoringDialog {
private static final String RECENT_KEYS = "ReplaceConstructorWithBuilder.RECENT_KEYS";
private static final String SETTER_PREFIX_KEY = "ConstructorWithBuilder.SetterPrefix";
protected ReplaceConstructorWithBuilderDialog(@NotNull Project project, PsiMethod[] constructors) {
super(project, false);
myConstructors = constructors;
@@ -95,21 +96,46 @@ public class ReplaceConstructorWithBuilderDialog extends RefactoringDialog {
final String className;
final String packageName;
if (myCreateBuilderClassRadioButton.isSelected()) {
boolean createNewBuilder = myCreateBuilderClassRadioButton.isSelected();
if (createNewBuilder) {
className = myNewClassName.getText().trim();
packageName = myPackageTextField.getText().trim();
} else {
final String fqName = myExistentClassTF.getText().trim();
className = StringUtil.getShortName(fqName);
packageName = StringUtil.getPackageName(fqName);
final PsiClass builderClass = JavaPsiFacade.getInstance(myProject).findClass(StringUtil.getQualifiedName(packageName, className), GlobalSearchScope.projectScope(myProject));
if (builderClass != null && !CommonRefactoringUtil.checkReadOnlyStatus(myProject, builderClass)) return;
}
final PsiClass builderClass = JavaPsiFacade.getInstance(myProject)
.findClass(StringUtil.getQualifiedName(packageName, className), GlobalSearchScope.projectScope(myProject));
if (!createNewBuilder) {
if (builderClass == null) {
String message = JavaRefactoringBundle.message("replace.constructor.builder.error.selected.class.was.not.found",
CommonRefactoringUtil.htmlEmphasize(className));
showErrorDialog(message);
return;
}
if (builderClass == myConstructors[0].getContainingClass()) {
showErrorDialog(JavaRefactoringBundle.message("replace.constructor.builder.error.builder.class.cannot.be.the.same",
CommonRefactoringUtil.htmlEmphasize(className)));
return;
}
}
else if (builderClass != null) {
showErrorDialog(JavaRefactoringBundle.message("replace.constructor.builder.error.class.with.chosen.name.already.exist",
CommonRefactoringUtil.htmlEmphasize(className),
CommonRefactoringUtil.htmlEmphasize(packageName)));
return;
}
if (!createNewBuilder && !CommonRefactoringUtil.checkReadOnlyStatus(myProject, builderClass)) return;
MoveDestination destination =
((DestinationFolderComboBox)myDestinationCb).selectDirectory(new PackageWrapper(myConstructors[0].getManager(), packageName), false);
invokeRefactoring(new ReplaceConstructorWithBuilderProcessor(getProject(), myConstructors, myParametersMap, className, packageName,
((DestinationFolderComboBox)myDestinationCb).selectDirectory(new PackageWrapper(myConstructors[0].getManager(), packageName), false),
myCreateBuilderClassRadioButton.isSelected()));
destination, createNewBuilder));
}
private void showErrorDialog(@NlsContexts.DialogMessage String message) {
Messages.showErrorDialog(getRootPane(), message, JavaRefactoringBundle.message("replace.constructor.with.builder"));
}
@Nullable
@Override
@@ -133,10 +159,9 @@ public class ReplaceConstructorWithBuilderDialog extends RefactoringDialog {
}
private void applyNewSetterPrefix() {
final String setterPrefix = Messages.showInputDialog(myTable, JavaRefactoringBundle
.message("constructor.with.builder.new.setter.prefix.dialog.message"), JavaRefactoringBundle
.message("constructor.with.builder.rename.setters.prefix.action.name"), null,
mySetterPrefix, new MySetterPrefixInputValidator());
String message = JavaRefactoringBundle.message("constructor.with.builder.new.setter.prefix.dialog.message");
String title = JavaRefactoringBundle.message("constructor.with.builder.rename.setters.prefix.action.name");
final String setterPrefix = Messages.showInputDialog(myTable, message, title, null, mySetterPrefix, new MySetterPrefixInputValidator());
if (setterPrefix != null) {
mySetterPrefix = setterPrefix;
PropertiesComponent.getInstance(myProject).setValue(SETTER_PREFIX_KEY, setterPrefix);
@@ -207,14 +232,16 @@ public class ReplaceConstructorWithBuilderDialog extends RefactoringDialog {
}
if (myCreateBuilderClassRadioButton.isSelected()) {
final String className = myNewClassName.getText().trim();
if (className.length() == 0 || !nameHelper.isQualifiedName(className)) throw new ConfigurationException(
if (className.isEmpty()) throw new ConfigurationException(null);
if (!nameHelper.isQualifiedName(className)) throw new ConfigurationException(
JavaRefactoringBundle.message("replace.constructor.builder.error.invalid.builder.class.name", className));
final String packageName = myPackageTextField.getText().trim();
if (packageName.length() > 0 && !nameHelper.isQualifiedName(packageName)) throw new ConfigurationException(
if (!packageName.isEmpty() && !nameHelper.isQualifiedName(packageName)) throw new ConfigurationException(
JavaRefactoringBundle.message("replace.constructor.builder.error.invalid.builder.package.name", packageName));
} else {
final String qualifiedName = myExistentClassTF.getText().trim();
if (qualifiedName.length() == 0 || !nameHelper.isQualifiedName(qualifiedName)) throw new ConfigurationException(
if (qualifiedName.isEmpty()) throw new ConfigurationException(null);
if (!nameHelper.isQualifiedName(qualifiedName)) throw new ConfigurationException(
JavaRefactoringBundle.message("replace.constructor.builder.error.invalid.builder.qualified.class.name", qualifiedName));
}
}

View File

@@ -1,4 +1,4 @@
// Copyright 2000-2022 JetBrains s.r.o. and contributors. Use of this source code is governed by the Apache 2.0 license.
// Copyright 2000-2024 JetBrains s.r.o. and contributors. Use of this source code is governed by the Apache 2.0 license.
package com.intellij.refactoring.replaceConstructorWithBuilder;
import com.intellij.ide.highlighter.JavaFileType;
@@ -8,6 +8,7 @@ import com.intellij.openapi.module.Module;
import com.intellij.openapi.module.ModuleUtilCore;
import com.intellij.openapi.project.Project;
import com.intellij.openapi.util.Comparing;
import com.intellij.openapi.util.NlsContexts;
import com.intellij.openapi.util.NlsSafe;
import com.intellij.openapi.util.Ref;
import com.intellij.openapi.util.text.StringUtil;
@@ -25,6 +26,7 @@ import com.intellij.refactoring.MoveDestination;
import com.intellij.refactoring.replaceConstructorWithBuilder.usageInfo.ReplaceConstructorWithSettersChainInfo;
import com.intellij.refactoring.util.FixableUsageInfo;
import com.intellij.refactoring.util.FixableUsagesRefactoringProcessor;
import com.intellij.refactoring.util.RefactoringUIUtil;
import com.intellij.usageView.UsageInfo;
import com.intellij.usageView.UsageViewDescriptor;
import com.intellij.util.CommonJavaRefactoringUtil;
@@ -45,8 +47,8 @@ public class ReplaceConstructorWithBuilderProcessor extends FixableUsagesRefacto
private final PsiMethod[] myConstructors;
private final Map<String, ParameterData> myParametersMap;
@NotNull
private final String myClassName;
private final String myPackageName;
private final @NlsSafe String myClassName;
private final @NlsSafe String myPackageName;
private final boolean myCreateNewBuilderClass;
private final PsiElementFactory myElementFactory;
private final MoveDestination myMoveDestination;
@@ -113,7 +115,6 @@ public class ReplaceConstructorWithBuilderProcessor extends FixableUsagesRefacto
}
if (directory != null) {
final CodeStyleManager codeStyleManager = CodeStyleManager.getInstance(PsiManager.getInstance(myProject).getProject());
final PsiJavaFile reformattedFile = (PsiJavaFile)codeStyleManager.reformat(JavaCodeStyleManager.getInstance(newFile.getProject()).shortenClassReferences(newFile));
@@ -125,7 +126,6 @@ public class ReplaceConstructorWithBuilderProcessor extends FixableUsagesRefacto
@Override
protected void performRefactoring(UsageInfo @NotNull [] usageInfos) {
final JavaPsiFacade psiFacade = JavaPsiFacade.getInstance(myProject);
final PsiClass builderClass = myCreateNewBuilderClass
? createBuilderClass()
@@ -222,19 +222,12 @@ public class ReplaceConstructorWithBuilderProcessor extends FixableUsagesRefacto
final PsiMethod constructor = getWorkingConstructor();
for (PsiParameter parameter : constructor.getParameterList().getParameters()) {
final String pureParamName = styleManager.variableNameToPropertyName(parameter.getName(), VariableKind.PARAMETER);
if (buf.length() > 0) buf.append(", ");
if (!buf.isEmpty()) buf.append(", ");
buf.append(myParametersMap.get(pureParamName).getFieldName());
}
return myElementFactory.createMethodFromText("public " +
constructor.getName() +
" " +
createMethodName +
"(){\n return new " +
constructor.getName() +
"(" +
buf +
")" +
";\n}", constructor);
return myElementFactory.createMethodFromText("public " + constructor.getName() + " " + createMethodName + "(){" +
"\n return new " + constructor.getName() + "(" + buf + ");" +
"\n}", constructor);
}
private PsiMethod getWorkingConstructor() {
@@ -284,30 +277,19 @@ public class ReplaceConstructorWithBuilderProcessor extends FixableUsagesRefacto
return "create" + StringUtil.capitalize(myConstructors[0].getName());
}
@Override
protected boolean preprocessUsages(@NotNull Ref<UsageInfo[]> refUsages) {
final MultiMap<PsiElement, String> conflicts = new MultiMap<>();
final JavaPsiFacade psiFacade = JavaPsiFacade.getInstance(myProject);
PsiClass anchor = myConstructors[0].getContainingClass();
final PsiClass builderClass =
psiFacade.findClass(StringUtil.getQualifiedName(myPackageName, myClassName), GlobalSearchScope.projectScope(myProject));
if (builderClass == null) {
if (!myCreateNewBuilderClass) {
conflicts.putValue(anchor, JavaRefactoringBundle.message("replace.constructor.builder.error.selected.class.was.not.found", myClassName));
}
} else if (myCreateNewBuilderClass){
conflicts.putValue(builderClass,
JavaRefactoringBundle.message("replace.constructor.builder.error.class.with.chosen.name.already.exist"));
}
final MultiMap<PsiElement, @NlsContexts.DialogMessage String> conflicts = new MultiMap<>();
if (myMoveDestination != null && myCreateNewBuilderClass) {
myMoveDestination.analyzeModuleConflicts(Collections.emptyList(), conflicts, refUsages.get());
}
final PsiMethod commonConstructor = getMostCommonConstructor();
if (commonConstructor == null) {
conflicts.putValue(anchor, JavaRefactoringBundle.message("replace.constructor.builder.error.no.constructor.chain"));
PsiClass containingClass = myConstructors[0].getContainingClass();
assert containingClass != null;
conflicts.putValue(containingClass, JavaRefactoringBundle.message("replace.constructor.builder.error.no.constructor.chain",
RefactoringUIUtil.getDescription(containingClass, false)));
}
return showConflicts(conflicts, refUsages.get());