mirror of
https://gitflic.ru/project/openide/openide.git
synced 2026-09-27 10:03:11 +07:00
Java: disallow making builder class from the constructor class itself (IDEA-254858)
GitOrigin-RevId: 071a0fd3c45f2b28b73b811e0d844a0f1b911372
This commit is contained in:
committed by
intellij-monorepo-bot
parent
cfc65a9890
commit
2870f49e0a
+40
-13
@@ -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));
|
||||
}
|
||||
}
|
||||
|
||||
+14
-32
@@ -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());
|
||||
|
||||
+7
-20
@@ -1,19 +1,4 @@
|
||||
/*
|
||||
* Copyright 2000-2017 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.
|
||||
*/
|
||||
|
||||
// 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.java.refactoring;
|
||||
|
||||
import com.intellij.JavaTestUtil;
|
||||
@@ -68,7 +53,7 @@ public class ReplaceConstructorWithBuilderTest extends LightMultiFileTestCase {
|
||||
}
|
||||
|
||||
public void testConstructorTree() {
|
||||
doTest(true, null, "Found constructors are not reducible to simple chain");
|
||||
doTest(true, null, "Constructors of class <b><code>Test</code></b> do not form a simple chain.");
|
||||
}
|
||||
|
||||
public void testGenerics() {
|
||||
@@ -117,13 +102,15 @@ public class ReplaceConstructorWithBuilderTest extends LightMultiFileTestCase {
|
||||
try {
|
||||
new ReplaceConstructorWithBuilderProcessor(getProject(), constructors, map, "Builder", packageName, null, createNewBuilderClass).run();
|
||||
if (conflicts != null) {
|
||||
fail("Conflicts were not detected:" + conflicts);
|
||||
fail("Conflicts were not detected: " + conflicts);
|
||||
}
|
||||
}
|
||||
catch (BaseRefactoringProcessor.ConflictsInTestsException e) {
|
||||
|
||||
if (conflicts == null) {
|
||||
fail("Conflict detected:" + e.getMessage());
|
||||
fail("Conflict detected: " + e.getMessage());
|
||||
}
|
||||
else if (!conflicts.equals(e.getMessage())) {
|
||||
fail("Conflicts do not match. Expected:\n" + conflicts + "\nActual:\n" + e.getMessage());
|
||||
}
|
||||
}
|
||||
});
|
||||
|
||||
@@ -765,16 +765,17 @@ replace.constructor.builder.setter.name.table.title=Setter Name
|
||||
replace.constructor.builder.field.name.table.title=Field Name
|
||||
replace.constructor.builder.parameter.table.title=Parameter
|
||||
replace.constructor.builder.select.builder.class.chooser.title=Select Builder Class
|
||||
replace.constructor.builder.error.invalid.builder.qualified.class.name=''{0}'' is invalid builder qualified class name
|
||||
replace.constructor.builder.error.invalid.builder.package.name=''{0}'' is invalid builder package name
|
||||
replace.constructor.builder.error.invalid.builder.class.name=''{0}'' is invalid builder class name
|
||||
replace.constructor.builder.error.invalid.builder.qualified.class.name=''{0}'' is not a valid fully qualified class name
|
||||
replace.constructor.builder.error.invalid.builder.package.name=''{0}'' is not a valid package name
|
||||
replace.constructor.builder.error.invalid.builder.class.name=''{0}'' is not a valid class name
|
||||
replace.constructor.builder.error.builder.class.cannot.be.the.same=Class {0} cannot be its own builder class.
|
||||
replace.constructor.builder.error.invalid.setter.name=''{0}'' is not a valid setter name
|
||||
replace.constructor.builder.error.invalid.field.name=''{0}'' is not a valid field name
|
||||
replace.constructor.builder.error.no.constructors=Current class doesn't have constructors to replace with builder.
|
||||
replace.constructor.builder.error.caret.position=The caret should be positioned inside a class whose constructors are to be replaced with builder.
|
||||
replace.constructor.builder.error.no.constructor.chain=Found constructors are not reducible to a simple chain
|
||||
replace.constructor.builder.error.class.with.chosen.name.already.exist=A class with the selected name already exist.
|
||||
replace.constructor.builder.error.selected.class.was.not.found=Selected builder class ''{0}'' was not found.
|
||||
replace.constructor.builder.error.no.constructor.chain=Constructors of {0} do not form a simple chain.
|
||||
replace.constructor.builder.error.class.with.chosen.name.already.exist=Class {0} already exists in package {1}.
|
||||
replace.constructor.builder.error.selected.class.was.not.found=Existing builder class {0} not found.
|
||||
replace.constructor.factory.error.invalid.factory.method.name=''{0}'' not a valid factory method name
|
||||
replace.constructor.factory.error.factory.method.already.exists=Factory method {0} already exists and will be used instead of the newly created.
|
||||
java.safe.delete.empty.callee.text=Callee text would be shown here
|
||||
|
||||
Reference in New Issue
Block a user