Skip to content

Commit 950284b

Browse files
committed
Add generic class/record support
1 parent d1be444 commit 950284b

File tree

7 files changed

+103
-48
lines changed

7 files changed

+103
-48
lines changed

build.gradle.kts

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -32,7 +32,7 @@ tasks {
3232

3333
patchPluginXml {
3434
sinceBuild.set("241")
35-
untilBuild.set("243.*")
35+
untilBuild.set("251.*")
3636
}
3737

3838
signPlugin {

src/main/java/com/github/junkfactory/innerbuilder/generators/BuilderClassGenerator.java

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -20,7 +20,7 @@ public GenerationResult generate() {
2020
//builder constructor
2121
var builderClass = builderClassParams.builderClass();
2222
var builderConstructor = generateBuilderConstructor();
23-
addMethod(builderClass, null, builderConstructor, false);
23+
addMethod(builderClass.psiClass(), null, builderConstructor, false);
2424

2525
var fieldsGenerator = generatorFactory.createBuilderFieldsGenerator(generatorParams, builderClassParams);
2626
fieldsGenerator.generate();
Lines changed: 6 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -1,18 +1,18 @@
11
package com.github.junkfactory.innerbuilder.generators;
22

3+
import com.github.junkfactory.innerbuilder.generators.InnerBuilderGenerator.BuilderClass;
34
import com.intellij.psi.PsiClass;
4-
import com.intellij.psi.PsiType;
55

6-
public record BuilderClassParams(PsiClass targetClass, PsiClass builderClass, PsiType builderType) {
6+
public record BuilderClassParams(PsiClass targetClass,
7+
BuilderClass builderClass) {
78

89
public static Builder builder() {
910
return new Builder();
1011
}
1112

1213
public static final class Builder {
1314
private PsiClass targetClass;
14-
private PsiClass builderClass;
15-
private PsiType builderType;
15+
private BuilderClass builderClass;
1616

1717
private Builder() {
1818
}
@@ -22,18 +22,13 @@ public Builder targetClass(PsiClass targetClass) {
2222
return this;
2323
}
2424

25-
public Builder builderClass(PsiClass builderClass) {
25+
public Builder builderClass(BuilderClass builderClass) {
2626
this.builderClass = builderClass;
2727
return this;
2828
}
2929

30-
public Builder builderType(PsiType builderType) {
31-
this.builderType = builderType;
32-
return this;
33-
}
34-
3530
public BuilderClassParams build() {
36-
return new BuilderClassParams(targetClass, builderClass, builderType);
31+
return new BuilderClassParams(targetClass, builderClass);
3732
}
3833
}
3934
}

src/main/java/com/github/junkfactory/innerbuilder/generators/BuilderFieldsGenerator.java

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -30,10 +30,11 @@ public List<PsiField> getFields() {
3030
public GenerationResult generate() {
3131
PsiField lastAddedField = null;
3232
for (var fieldMember : generatorParams.psi().selectedFields()) {
33-
lastAddedField = createOrUpdateField(builderClassParams.builderClass(), fieldMember, lastAddedField);
33+
lastAddedField =
34+
createOrUpdateField(builderClassParams.builderClass().psiClass(), fieldMember, lastAddedField);
3435
fields.add(lastAddedField);
3536
}
36-
cleanupFields(builderClassParams.builderClass());
37+
cleanupFields(builderClassParams.builderClass().psiClass());
3738
return GenerationResult.NO_RESULT;
3839
}
3940

src/main/java/com/github/junkfactory/innerbuilder/generators/BuilderMethodsGenerator.java

Lines changed: 13 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -14,6 +14,9 @@
1414

1515
class BuilderMethodsGenerator extends AbstractGenerator implements MethodsGenerator {
1616

17+
public record BuilderClassName(String className, String instanceClassName) {
18+
}
19+
1720
private final BuilderClassParams builderClassParams;
1821
private final FieldsGenerator fieldsGenerator;
1922

@@ -38,17 +41,17 @@ public GenerationResult generate() {
3841
for (var field : fieldsGenerator.getFields()) {
3942
var setterMethod = generateFieldMethod(field);
4043
field.putCopyableUserData(UserDataKey.METHOD_REF, setterMethod.getName());
41-
lastAddedElement = addMethod(builderClass, lastAddedElement, setterMethod, false);
44+
lastAddedElement = addMethod(builderClass.psiClass(), lastAddedElement, setterMethod, false);
4245
}
4346

4447
var options = generatorParams.options();
4548
if (options.contains(JavaInnerBuilderOption.WITH_VALIDATE_METHOD)) {
4649
var validateMethod = generateValidateMethod();
47-
addMethod(builderClass, lastAddedElement, validateMethod, false);
50+
addMethod(builderClass.psiClass(), lastAddedElement, validateMethod, false);
4851
}
4952

5053
var buildMethod = generateBuildMethod(targetClass);
51-
addMethod(builderClass, null, buildMethod, builderClassParams.targetClass().isRecord());
54+
addMethod(builderClass.psiClass(), null, buildMethod, builderClassParams.targetClass().isRecord());
5255
return generationResult;
5356
}
5457

@@ -92,7 +95,7 @@ private PsiMethod generatePutToMap(PsiField field, PsiMethod fieldPutMethod) {
9295
if (isPublic) {
9396
methodText.append(PsiModifier.PUBLIC).append(' ');
9497
}
95-
methodText.append(BUILDER_CLASS_NAME)
98+
methodText.append(builderClassParams.builderClass().builderType().getPresentableText())
9699
.append(' ')
97100
.append(methodName)
98101
.append('(')
@@ -130,7 +133,7 @@ private PsiMethod generateAddToCollection(PsiField field, PsiMethod fieldAddMeth
130133
if (isPublic) {
131134
methodText.append(PsiModifier.PUBLIC).append(' ');
132135
}
133-
methodText.append(BUILDER_CLASS_NAME)
136+
methodText.append(builderClassParams.builderClass().builderType().getPresentableText())
134137
.append(' ')
135138
.append(methodName)
136139
.append('(')
@@ -159,7 +162,7 @@ private PsiMethod generateBuilderSetter(PsiField field) {
159162
if (isPublic) {
160163
methodText.append(PsiModifier.PUBLIC).append(' ');
161164
}
162-
methodText.append(BUILDER_CLASS_NAME)
165+
methodText.append(builderClassParams.builderClass().builderType().getPresentableText())
163166
.append(' ')
164167
.append(fieldName)
165168
.append('(')
@@ -180,10 +183,11 @@ private PsiMethod generateBuilderSetter(PsiField field) {
180183
}
181184

182185
private PsiMethod generateBuildMethod(PsiClass targetClass) {
186+
var targetClassName = Utils.buildClassName(targetClass.getName(), targetClass);
183187
var buildMethod = new StringBuilder()
184188
.append(isPublic ? PsiModifier.PUBLIC : EMPTY)
185189
.append(isPublic ? SPACE : EMPTY)
186-
.append(targetClass.getName())
190+
.append(targetClassName.className())
187191
.append(" build() {");
188192
if (generatorParams.options().contains(JavaInnerBuilderOption.WITH_VALIDATE_METHOD)) {
189193
buildMethod.append("validate();");
@@ -193,13 +197,13 @@ private PsiMethod generateBuildMethod(PsiClass targetClass) {
193197
.map(PsiField::getName)
194198
.collect(Collectors.joining(", "));
195199
buildMethod.append("return new ")
196-
.append(targetClass.getName())
200+
.append(targetClassName.instanceClassName())
197201
.append("(")
198202
.append(recordParameters)
199203
.append(");");
200204
} else {
201205
buildMethod.append("return new ")
202-
.append(targetClass.getName())
206+
.append(targetClassName.instanceClassName())
203207
.append("(this);");
204208
}
205209
buildMethod.append("}");

src/main/java/com/github/junkfactory/innerbuilder/generators/InnerBuilderGenerator.java

Lines changed: 29 additions & 21 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,6 @@
11
package com.github.junkfactory.innerbuilder.generators;
22

3+
import com.github.junkfactory.innerbuilder.generators.BuilderMethodsGenerator.BuilderClassName;
34
import com.github.junkfactory.innerbuilder.ui.JavaInnerBuilderOption;
45
import com.intellij.codeInsight.generation.PsiFieldMember;
56
import com.intellij.psi.PsiClass;
@@ -22,6 +23,12 @@
2223

2324
class InnerBuilderGenerator extends AbstractGenerator implements Generator {
2425

26+
public record BuilderClass(PsiClass psiClass,
27+
PsiType builderType,
28+
BuilderClassName builderClassName,
29+
boolean genericType) {
30+
}
31+
2532
InnerBuilderGenerator(GeneratorFactory generatorFactory, GeneratorParams generatorParams) {
2633
super(generatorFactory, generatorParams);
2734
}
@@ -33,54 +40,51 @@ public GenerationResult generate() {
3340
if (targetClass == null || BUILDER_CLASS_NAME.equals(targetClass.getName())) {
3441
return NO_RESULT;
3542
}
36-
var psiElementFactory = generatorParams.psi().factory();
3743
var builderClass = findOrCreateBuilderClass(targetClass);
38-
var builderType = psiElementFactory.createTypeFromText(BUILDER_CLASS_NAME, targetClass);
3944

4045
if (!targetClass.isRecord()) {
41-
var constructor = generateTargetConstructor(targetClass, builderType);
46+
var constructor = generateTargetConstructor(targetClass, builderClass);
4247
addMethod(targetClass, null, constructor, true);
4348
}
4449

45-
var newBuilderMethod = generateStaticBuilderMethod(targetClass, builderType);
50+
var newBuilderMethod = generateStaticBuilderMethod(targetClass, builderClass);
4651
addMethod(targetClass, null, newBuilderMethod, false);
4752

4853
// toBuilder method
4954
var options = generatorParams.options();
5055
if (options.contains(JavaInnerBuilderOption.WITH_TO_BUILDER_METHOD)) {
51-
var toBuilderMethod = generateToBuilderMethod(targetClass, builderType,
56+
var toBuilderMethod = generateToBuilderMethod(targetClass, builderClass,
5257
generatorParams.psi().selectedFields());
5358
addMethod(targetClass, null, toBuilderMethod, true);
5459
}
5560

5661
var params = BuilderClassParams.builder()
5762
.targetClass(targetClass)
5863
.builderClass(builderClass)
59-
.builderType(builderType)
6064
.build();
6165
var result = generatorFactory.createBuilderClassGenerator(generatorParams, params).generate();
6266
generationResult.merge(result);
6367
var codeStyleManager = generatorParams.psi().codeStyleManager();
6468
generationResult.when(ANNOTATIONS_ADDED, () -> codeStyleManager.shortenClassReferences(targetClass));
6569
generationResult.when(IMPORTS_ADDED, () -> codeStyleManager.removeRedundantImports((PsiJavaFile) file));
66-
CodeStyleManager.getInstance(generatorParams.project()).reformat(builderClass);
70+
CodeStyleManager.getInstance(generatorParams.project()).reformat(builderClass.psiClass());
6771
return generationResult;
6872
}
6973

7074
private PsiMethod generateToBuilderMethod(PsiClass targetClass,
71-
PsiType builderType,
75+
BuilderClass builderClass,
7276
Collection<PsiFieldMember> fields) {
7377
var targetModifierList = Objects.requireNonNull(targetClass.getModifierList());
7478
boolean isPublic = targetModifierList.hasModifierProperty(PsiModifier.PUBLIC);
7579
var toBuilderMethod = new StringBuilder()
7680
.append(isPublic ? PsiModifier.PUBLIC : EMPTY)
7781
.append(isPublic ? SPACE : EMPTY)
78-
.append(builderType.getPresentableText())
82+
.append(builderClass.builderType().getPresentableText())
7983
.append(SPACE)
8084
.append(TO_BUILDER_NAME)
8185
.append("() {")
8286
.append("var builder = new ")
83-
.append(builderType.getPresentableText())
87+
.append(builderClass.builderType().getPresentableText())
8488
.append("();");
8589
for (var member : fields) {
8690
var field = member.getElement();
@@ -97,9 +101,10 @@ private PsiMethod generateToBuilderMethod(PsiClass targetClass,
97101
return psiElementFactory.createMethodFromText(toBuilderMethod.toString(), targetClass);
98102
}
99103

100-
private PsiMethod generateStaticBuilderMethod(PsiClass targetClass, PsiType builderType) {
104+
private PsiMethod generateStaticBuilderMethod(PsiClass targetClass, BuilderClass builderClass) {
101105
var psiElementFactory = generatorParams.psi().factory();
102-
var newBuilderMethod = psiElementFactory.createMethod(BUILDER_METHOD_NAME, builderType);
106+
var methodName = Utils.buildBuilderMethodName(builderClass.builderType());
107+
var newBuilderMethod = psiElementFactory.createMethodFromText(methodName, targetClass);
103108
PsiUtil.setModifierProperty(newBuilderMethod, PsiModifier.STATIC, true);
104109
PsiUtil.setModifierProperty(newBuilderMethod, PsiModifier.PUBLIC, true);
105110

@@ -108,18 +113,18 @@ private PsiMethod generateStaticBuilderMethod(PsiClass targetClass, PsiType buil
108113
existingMethod = newBuilderMethod;
109114
var newBuilderMethodBody = Objects.requireNonNull(existingMethod.getBody());
110115
var newStatement = psiElementFactory.createStatementFromText(String.format(
111-
"return new %s();", builderType.getPresentableText()), newBuilderMethod);
116+
"return new %s();", builderClass.builderClassName().instanceClassName()), newBuilderMethod);
112117
newBuilderMethodBody.add(newStatement);
113118
}
114119
return existingMethod;
115120
}
116121

117-
private PsiMethod generateTargetConstructor(final PsiClass targetClass, final PsiType builderType) {
122+
private PsiMethod generateTargetConstructor(final PsiClass targetClass, BuilderClass builderClass) {
118123
var constructor = new StringBuilder()
119124
.append("private ")
120125
.append(targetClass.getName())
121126
.append("(")
122-
.append(builderType.getPresentableText())
127+
.append(builderClass.builderType().getPresentableText())
123128
.append(" builder) {");
124129

125130
for (var member : generatorParams.psi().selectedFields()) {
@@ -151,19 +156,22 @@ private PsiMethod generateTargetConstructor(final PsiClass targetClass, final Ps
151156
}
152157

153158
@NotNull
154-
private PsiClass findOrCreateBuilderClass(final PsiClass targetClass) {
155-
var builderClass = targetClass.findInnerClassByName(BUILDER_CLASS_NAME, false);
159+
private BuilderClass findOrCreateBuilderClass(final PsiClass targetClass) {
160+
var builderClassName = Utils.buildClassName(BUILDER_CLASS_NAME, targetClass);
161+
var builderClass = targetClass.findInnerClassByName(builderClassName.className(), false);
156162
if (builderClass == null) {
157-
return (PsiClass) targetClass.add(createBuilderClass(targetClass));
163+
builderClass = (PsiClass) targetClass.add(createBuilderClass(targetClass, builderClassName.className()));
158164
}
159165

160-
return builderClass;
166+
var psiElementFactory = generatorParams.psi().factory();
167+
var builderType = psiElementFactory.createTypeFromText(builderClassName.className(), targetClass);
168+
return new BuilderClass(builderClass, builderType, builderClassName, Utils.isGenericType(builderType));
161169
}
162170

163171
@NotNull
164-
private PsiClass createBuilderClass(final PsiClass targetClass) {
172+
private PsiClass createBuilderClass(final PsiClass targetClass, String builderClassName) {
165173
String classDef = "public static final class " +
166-
BUILDER_CLASS_NAME +
174+
builderClassName +
167175
" {}" +
168176
System.lineSeparator();
169177
return generatorParams.psi().factory().createClassFromText(classDef, targetClass)

src/main/java/com/github/junkfactory/innerbuilder/generators/Utils.java

Lines changed: 50 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,9 @@
11
package com.github.junkfactory.innerbuilder.generators;
22

3+
import com.github.junkfactory.innerbuilder.generators.BuilderMethodsGenerator.BuilderClassName;
34
import com.intellij.openapi.editor.Editor;
45
import com.intellij.psi.PsiClass;
6+
import com.intellij.psi.PsiClassType;
57
import com.intellij.psi.PsiField;
68
import com.intellij.psi.PsiFile;
79
import com.intellij.psi.PsiMethod;
@@ -19,6 +21,8 @@
1921
import java.util.Optional;
2022
import java.util.function.Predicate;
2123

24+
import static com.github.junkfactory.innerbuilder.generators.AbstractGenerator.BUILDER_CLASS_NAME;
25+
2226
public class Utils {
2327
@NonNls
2428
static final String JAVA_DOT_LANG = "java.lang.";
@@ -147,9 +151,52 @@ public static List<String> stringToList(String str) {
147151
.toList();
148152
}
149153

150-
public static String parseType(String text) {
151-
var parenthesisIndex = text.indexOf('(');
152-
return parenthesisIndex == -1 ? text : text.substring(0, parenthesisIndex);
154+
public static boolean isGenericType(PsiType psiType) {
155+
// Check if the type is a PsiClassType
156+
if (psiType instanceof PsiClassType classType) {
157+
// Check if it has type parameters
158+
return classType.getParameters().length > 0;
159+
}
160+
return false;
161+
}
162+
163+
public static BuilderClassName buildClassName(String className, PsiClass targetClass) {
164+
var builderClassName = new StringBuilder(className);
165+
var typeParameters = targetClass.getTypeParameters();
166+
if (typeParameters.length > 0) {
167+
builderClassName.append('<');
168+
for (int i = 0, l = typeParameters.length; i < l; i++) {
169+
var typeParameter = typeParameters[i];
170+
builderClassName.append(typeParameter.getName());
171+
if (i < l - 1) {
172+
builderClassName.append(", ");
173+
}
174+
}
175+
builderClassName.append('>');
176+
return new BuilderClassName(builderClassName.toString(), "%s<>".formatted(className));
177+
}
178+
return new BuilderClassName(builderClassName.toString(), className);
179+
}
180+
181+
public static String buildBuilderMethodName(PsiType builderType) {
182+
var methodName = BUILDER_CLASS_NAME;
183+
if (isGenericType(builderType)) {
184+
var psiClassType = (PsiClassType) builderType;
185+
var typeParameters = psiClassType.getParameters();
186+
if (typeParameters.length > 0) {
187+
var typeParameterNames = new StringBuilder();
188+
for (int i = 0, l = typeParameters.length; i < l; i++) {
189+
var typeParameter = typeParameters[i];
190+
typeParameterNames.append(typeParameter.getPresentableText());
191+
if (i < l - 1) {
192+
typeParameterNames.append(", ");
193+
}
194+
}
195+
methodName = String.format("<%s> %s %s(){}", typeParameterNames,
196+
builderType.getPresentableText(), methodName);
197+
}
198+
}
199+
return methodName;
153200
}
154201

155202
}

0 commit comments

Comments
 (0)