diff --git a/java/java-impl-refactorings/src/com/intellij/refactoring/replaceConstructorWithBuilder/ReplaceConstructorWithBuilderProcessor.java b/java/java-impl-refactorings/src/com/intellij/refactoring/replaceConstructorWithBuilder/ReplaceConstructorWithBuilderProcessor.java index b93144ff0ef5..3e4b903f7a45 100644 --- a/java/java-impl-refactorings/src/com/intellij/refactoring/replaceConstructorWithBuilder/ReplaceConstructorWithBuilderProcessor.java +++ b/java/java-impl-refactorings/src/com/intellij/refactoring/replaceConstructorWithBuilder/ReplaceConstructorWithBuilderProcessor.java @@ -13,6 +13,7 @@ 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; +import com.intellij.pom.java.JavaFeature; import com.intellij.psi.*; import com.intellij.psi.codeStyle.CodeStyleManager; import com.intellij.psi.codeStyle.JavaCodeStyleManager; @@ -40,6 +41,8 @@ import org.jetbrains.annotations.Nullable; import java.util.Collections; import java.util.List; import java.util.Map; +import java.util.Objects; +import java.util.stream.Stream; /** * @author anna @@ -146,7 +149,7 @@ public class ReplaceConstructorWithBuilderProcessor extends FixableUsagesRefacto super.performRefactoring(usageInfos); - final PsiMethod method = createMethodSignature(createMethodName()); + final PsiMethod method = createBuildMethod(createMethodName()); if (builderClass.findMethodBySignature(method, false) == null) { builderClass.add(method); } @@ -225,18 +228,44 @@ public class ReplaceConstructorWithBuilderProcessor extends FixableUsagesRefacto } } - private PsiMethod createMethodSignature(String createMethodName) { + private PsiMethod createBuildMethod(String createMethodName) { JavaCodeStyleManager styleManager = JavaCodeStyleManager.getInstance(myProject); - final StringBuilder buf = new StringBuilder(); + + final PsiClass aClass = myConstructors[0].getContainingClass(); + assert aClass != null; + PsiType[] typeArguments = Stream.of(aClass.getTypeParameters()) + .map(myElementFactory::createType) + .toArray(PsiType[]::new); + PsiMethod method = myElementFactory.createMethod(createMethodName, myElementFactory.createType(aClass, typeArguments)); + + final StringBuilder bodyText = new StringBuilder(); final PsiMethod constructor = getWorkingConstructor(); + bodyText.append("{\n return new ").append(constructor.getName()); + if (typeArguments.length > 0) { + if (PsiUtil.isAvailable(JavaFeature.DIAMOND_TYPES, aClass)) { + bodyText.append("<>"); + } + else { + bodyText.append('<'); + boolean comma = false; + for (PsiType argument : typeArguments) { + bodyText.append(comma ? ", " : "").append(argument.getCanonicalText()); + comma = true; + } + bodyText.append('>'); + } + } + bodyText.append("("); + boolean comma = false; for (PsiParameter parameter : constructor.getParameterList().getParameters()) { final String pureParamName = styleManager.variableNameToPropertyName(parameter.getName(), VariableKind.PARAMETER); - if (!buf.isEmpty()) buf.append(", "); - buf.append(myParametersMap.get(pureParamName).getFieldName()); + bodyText.append(comma ? ", " : "").append(myParametersMap.get(pureParamName).getFieldName()); + comma = true; } - return myElementFactory.createMethodFromText("public " + constructor.getName() + " " + createMethodName + "(){" + - "\n return new " + constructor.getName() + "(" + buf + ");" + - "\n}", constructor); + bodyText.append(");\n}"); + PsiCodeBlock body = myElementFactory.createCodeBlockFromText(bodyText.toString(), method); + Objects.requireNonNull(method.getBody()).replace(body); + return method; } private PsiMethod getWorkingConstructor() { diff --git a/java/java-impl-refactorings/src/com/intellij/refactoring/replaceConstructorWithBuilder/usageInfo/ReplaceConstructorWithSettersChainInfo.java b/java/java-impl-refactorings/src/com/intellij/refactoring/replaceConstructorWithBuilder/usageInfo/ReplaceConstructorWithSettersChainInfo.java index d7de96ad13f1..e97d6ed32743 100644 --- a/java/java-impl-refactorings/src/com/intellij/refactoring/replaceConstructorWithBuilder/usageInfo/ReplaceConstructorWithSettersChainInfo.java +++ b/java/java-impl-refactorings/src/com/intellij/refactoring/replaceConstructorWithBuilder/usageInfo/ReplaceConstructorWithSettersChainInfo.java @@ -1,19 +1,4 @@ -/* - * 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. - */ - +// 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.usageInfo; import com.intellij.openapi.util.Comparing; @@ -21,11 +6,13 @@ import com.intellij.openapi.util.text.StringUtil; import com.intellij.psi.*; import com.intellij.psi.codeStyle.JavaCodeStyleManager; import com.intellij.psi.codeStyle.VariableKind; +import com.intellij.psi.impl.PsiDiamondTypeUtil; import com.intellij.refactoring.replaceConstructorWithBuilder.ParameterData; import com.intellij.refactoring.util.FixableUsageInfo; import com.intellij.util.IncorrectOperationException; import java.util.Map; +import java.util.Objects; public class ReplaceConstructorWithSettersChainInfo extends FixableUsageInfo { private final String myBuilderClass; @@ -65,9 +52,11 @@ public class ReplaceConstructorWithSettersChainInfo extends FixableUsageInfo { buf.append(data.getSetterName()).append("(").append(arg).append(")."); } } - + PsiNewExpression expression = (PsiNewExpression)PsiDiamondTypeUtil.expandTopLevelDiamondsInside(expr); + PsiReferenceParameterList typeArguments = Objects.requireNonNull(expression.getClassReference()).getParameterList(); + assert typeArguments != null; final PsiExpression settersChain = elementFactory.createExpressionFromText( - "new " + myBuilderClass + "()." + buf + "create" + StringUtil.capitalize(constructor.getName()) + "()", + "new " + myBuilderClass + typeArguments.getText() + "()." + buf + "create" + StringUtil.capitalize(constructor.getName()) + "()", null); styleManager.shortenClassReferences(expr.replace(settersChain)); diff --git a/java/java-psi-api/src/com/intellij/psi/util/PropertyUtilBase.java b/java/java-psi-api/src/com/intellij/psi/util/PropertyUtilBase.java index 3cf0c105257b..bc588d4c83e7 100644 --- a/java/java-psi-api/src/com/intellij/psi/util/PropertyUtilBase.java +++ b/java/java-psi-api/src/com/intellij/psi/util/PropertyUtilBase.java @@ -1,4 +1,4 @@ -// Copyright 2000-2019 JetBrains s.r.o. Use of this source code is governed by the Apache 2.0 license that can be found in the LICENSE file. +// 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.psi.util; import com.intellij.codeInsight.AnnotationTargetUtil; @@ -21,6 +21,7 @@ import org.jetbrains.annotations.Nullable; import java.beans.Introspector; import java.util.*; +import java.util.stream.Stream; public class PropertyUtilBase { @@ -604,9 +605,17 @@ public class PropertyUtilBase { VariableKind kind = codeStyleManager.getVariableKind(field); String propertyName = codeStyleManager.variableNameToPropertyName(name, kind); String setName = suggestSetterName(field); - PsiMethod setMethod = factory - .createMethodFromText(factory.createMethod(setName, returnSelf ? factory.createType(containingClass) : PsiTypes.voidType()).getText(), - field); + + PsiMethod setMethod; + if (returnSelf) { + PsiType[] typeArguments = Stream.of(containingClass.getTypeParameters()) + .map(factory::createType) + .toArray(PsiType[]::new); + setMethod = factory.createMethod(setName, factory.createType(containingClass, typeArguments)); + } + else { + setMethod = factory.createMethod(setName, PsiTypes.voidType()); + } String parameterName = codeStyleManager.propertyNameToVariableName(propertyName, VariableKind.PARAMETER); PsiParameter param = factory.createParameter(parameterName, AnnotationTargetUtil.keepStrictlyTypeUseAnnotations(field.getModifierList(), field.getType())); diff --git a/java/java-tests/testData/refactoring/replaceConstructorWithBuilder/generics/after/Builder.java b/java/java-tests/testData/refactoring/replaceConstructorWithBuilder/generics/after/Builder.java index ce435c341903..9a0bac1b62dd 100644 --- a/java/java-tests/testData/refactoring/replaceConstructorWithBuilder/generics/after/Builder.java +++ b/java/java-tests/testData/refactoring/replaceConstructorWithBuilder/generics/after/Builder.java @@ -1,12 +1,12 @@ public class Builder { private T t; - public Builder setT(T t) { + public Builder setT(T t) { this.t = t; return this; } - public Test createTest() { - return new Test(t); + public Test createTest() { + return new Test<>(t); } } \ No newline at end of file diff --git a/java/java-tests/testData/refactoring/replaceConstructorWithBuilder/generics/after/Test.java b/java/java-tests/testData/refactoring/replaceConstructorWithBuilder/generics/after/Test.java index 732c6d1b6c01..6d8dc5f0fea6 100644 --- a/java/java-tests/testData/refactoring/replaceConstructorWithBuilder/generics/after/Test.java +++ b/java/java-tests/testData/refactoring/replaceConstructorWithBuilder/generics/after/Test.java @@ -1,7 +1,7 @@ public class Test { public Test(T t){} void foo(){} - public static void main(T args){ - new Builder().setT(args).createTest().foo(); + public static void main(String[] args){ + new Builder().setT(args[0]).createTest().foo(); } } \ No newline at end of file diff --git a/java/java-tests/testData/refactoring/replaceConstructorWithBuilder/generics/before/Test.java b/java/java-tests/testData/refactoring/replaceConstructorWithBuilder/generics/before/Test.java index 4bf62855f7e0..810592baf934 100644 --- a/java/java-tests/testData/refactoring/replaceConstructorWithBuilder/generics/before/Test.java +++ b/java/java-tests/testData/refactoring/replaceConstructorWithBuilder/generics/before/Test.java @@ -1,7 +1,7 @@ public class Test { public Test(T t){} void foo(){} - public static void main(T args){ - new Test(args).foo(); + public static void main(String[] args){ + new Test<>(args[0]).foo(); } } \ No newline at end of file diff --git a/java/java-tests/testData/refactoring/replaceConstructorWithBuilder/genericsImport/after/Builder.java b/java/java-tests/testData/refactoring/replaceConstructorWithBuilder/genericsImport/after/Builder.java index 8e08ac09af54..2aa58882cc12 100644 --- a/java/java-tests/testData/refactoring/replaceConstructorWithBuilder/genericsImport/after/Builder.java +++ b/java/java-tests/testData/refactoring/replaceConstructorWithBuilder/genericsImport/after/Builder.java @@ -4,17 +4,17 @@ public class Builder { private String name; private Class clazz; - public Builder setName(String name) { + public Builder setName(String name) { this.name = name; return this; } - public Builder setClazz(Class clazz) { + public Builder setClazz(Class clazz) { this.clazz = clazz; return this; } - public Test createTest() { - return new Test(name, clazz); + public Test createTest() { + return new Test<>(name, clazz); } } \ No newline at end of file diff --git a/java/java-tests/testData/refactoring/replaceConstructorWithBuilder/genericsImport/after/Test.java b/java/java-tests/testData/refactoring/replaceConstructorWithBuilder/genericsImport/after/Test.java index b26e2b148eda..dcd4a46578ec 100644 --- a/java/java-tests/testData/refactoring/replaceConstructorWithBuilder/genericsImport/after/Test.java +++ b/java/java-tests/testData/refactoring/replaceConstructorWithBuilder/genericsImport/after/Test.java @@ -9,5 +9,5 @@ public class Test { this.clazz = clazz; } - public static final Test BOOLEAN = new Builder().setName("b").setClazz(Boolean.class).createTest(); + public static final Test BOOLEAN = new Builder().setName("b").setClazz(Boolean.class).createTest(); } \ No newline at end of file