Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion build.gradle.kts
Original file line number Diff line number Diff line change
Expand Up @@ -32,7 +32,7 @@ tasks {

patchPluginXml {
sinceBuild.set("241")
untilBuild.set("243.*")
untilBuild.set("251.*")
}

signPlugin {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -66,6 +66,11 @@ protected boolean addImport(PsiType psiType) {
return generatorParams.psi().codeStyleManager().addImport((PsiJavaFile) generatorParams.psi().file(), psiClass);
}

protected PsiMethod findFirstConstructor(PsiClass target) {
var constructors = target.getConstructors();
return constructors.length > 0 ? constructors[0] : null;
}

private PsiMethod findConstructor(PsiClass target, PsiMethod newMethod) {
for (var constructor : target.getConstructors()) {
if (Utils.areParameterListsEqual(constructor.getParameterList(), newMethod.getParameterList())) {
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,11 @@
package com.github.junkfactory.innerbuilder.generators;

import com.intellij.psi.PsiClass;
import com.intellij.psi.PsiType;

public record BuilderClass(PsiClass psiClass,
PsiType builderType,
BuilderClassName builderClassName,
boolean genericType) {
}

Original file line number Diff line number Diff line change
Expand Up @@ -20,7 +20,7 @@ public GenerationResult generate() {
//builder constructor
var builderClass = builderClassParams.builderClass();
var builderConstructor = generateBuilderConstructor();
addMethod(builderClass, null, builderConstructor, false);
addMethod(builderClass.psiClass(), null, builderConstructor, false);

var fieldsGenerator = generatorFactory.createBuilderFieldsGenerator(generatorParams, builderClassParams);
fieldsGenerator.generate();
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
package com.github.junkfactory.innerbuilder.generators;

public record BuilderClassName(String className, String instanceClassName) {
}

Original file line number Diff line number Diff line change
@@ -1,18 +1,17 @@
package com.github.junkfactory.innerbuilder.generators;

import com.intellij.psi.PsiClass;
import com.intellij.psi.PsiType;

public record BuilderClassParams(PsiClass targetClass, PsiClass builderClass, PsiType builderType) {
public record BuilderClassParams(PsiClass targetClass,
BuilderClass builderClass) {

public static Builder builder() {
return new Builder();
}

public static final class Builder {
private PsiClass targetClass;
private PsiClass builderClass;
private PsiType builderType;
private BuilderClass builderClass;

private Builder() {
}
Expand All @@ -22,18 +21,13 @@ public Builder targetClass(PsiClass targetClass) {
return this;
}

public Builder builderClass(PsiClass builderClass) {
public Builder builderClass(BuilderClass builderClass) {
this.builderClass = builderClass;
return this;
}

public Builder builderType(PsiType builderType) {
this.builderType = builderType;
return this;
}

public BuilderClassParams build() {
return new BuilderClassParams(targetClass, builderClass, builderType);
return new BuilderClassParams(targetClass, builderClass);
}
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -30,10 +30,11 @@ public List<PsiField> getFields() {
public GenerationResult generate() {
PsiField lastAddedField = null;
for (var fieldMember : generatorParams.psi().selectedFields()) {
lastAddedField = createOrUpdateField(builderClassParams.builderClass(), fieldMember, lastAddedField);
lastAddedField =
createOrUpdateField(builderClassParams.builderClass().psiClass(), fieldMember, lastAddedField);
fields.add(lastAddedField);
}
cleanupFields(builderClassParams.builderClass());
cleanupFields(builderClassParams.builderClass().psiClass());
return GenerationResult.NO_RESULT;
}

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -34,21 +34,21 @@ public GenerationResult generate() {
var targetClass = builderClassParams.targetClass();
var targetModifierList = Objects.requireNonNull(targetClass.getModifierList());
isPublic = targetModifierList.hasModifierProperty(PsiModifier.PUBLIC);
PsiElement lastAddedElement = null;
PsiElement lastAddedElement = findFirstConstructor(builderClass.psiClass());
for (var field : fieldsGenerator.getFields()) {
var setterMethod = generateFieldMethod(field);
field.putCopyableUserData(UserDataKey.METHOD_REF, setterMethod.getName());
lastAddedElement = addMethod(builderClass, lastAddedElement, setterMethod, false);
lastAddedElement = addMethod(builderClass.psiClass(), lastAddedElement, setterMethod, false);
}

var options = generatorParams.options();
if (options.contains(JavaInnerBuilderOption.WITH_VALIDATE_METHOD)) {
var validateMethod = generateValidateMethod();
addMethod(builderClass, lastAddedElement, validateMethod, false);
lastAddedElement = addMethod(builderClass.psiClass(), lastAddedElement, validateMethod, false);
}

var buildMethod = generateBuildMethod(targetClass);
addMethod(builderClass, null, buildMethod, builderClassParams.targetClass().isRecord());
addMethod(builderClass.psiClass(), lastAddedElement, buildMethod, builderClassParams.targetClass().isRecord());
return generationResult;
}

Expand Down Expand Up @@ -92,7 +92,7 @@ private PsiMethod generatePutToMap(PsiField field, PsiMethod fieldPutMethod) {
if (isPublic) {
methodText.append(PsiModifier.PUBLIC).append(' ');
}
methodText.append(BUILDER_CLASS_NAME)
methodText.append(builderClassParams.builderClass().builderType().getPresentableText())
.append(' ')
.append(methodName)
.append('(')
Expand Down Expand Up @@ -130,7 +130,7 @@ private PsiMethod generateAddToCollection(PsiField field, PsiMethod fieldAddMeth
if (isPublic) {
methodText.append(PsiModifier.PUBLIC).append(' ');
}
methodText.append(BUILDER_CLASS_NAME)
methodText.append(builderClassParams.builderClass().builderType().getPresentableText())
.append(' ')
.append(methodName)
.append('(')
Expand Down Expand Up @@ -159,7 +159,7 @@ private PsiMethod generateBuilderSetter(PsiField field) {
if (isPublic) {
methodText.append(PsiModifier.PUBLIC).append(' ');
}
methodText.append(BUILDER_CLASS_NAME)
methodText.append(builderClassParams.builderClass().builderType().getPresentableText())
.append(' ')
.append(fieldName)
.append('(')
Expand All @@ -180,10 +180,11 @@ private PsiMethod generateBuilderSetter(PsiField field) {
}

private PsiMethod generateBuildMethod(PsiClass targetClass) {
var targetClassName = Utils.buildClassName(targetClass.getName(), targetClass);
var buildMethod = new StringBuilder()
.append(isPublic ? PsiModifier.PUBLIC : EMPTY)
.append(isPublic ? SPACE : EMPTY)
.append(targetClass.getName())
.append(targetClassName.className())
.append(" build() {");
if (generatorParams.options().contains(JavaInnerBuilderOption.WITH_VALIDATE_METHOD)) {
buildMethod.append("validate();");
Expand All @@ -193,13 +194,13 @@ private PsiMethod generateBuildMethod(PsiClass targetClass) {
.map(PsiField::getName)
.collect(Collectors.joining(", "));
buildMethod.append("return new ")
.append(targetClass.getName())
.append(targetClassName.instanceClassName())
.append("(")
.append(recordParameters)
.append(");");
} else {
buildMethod.append("return new ")
.append(targetClass.getName())
.append(targetClassName.instanceClassName())
.append("(this);");
}
buildMethod.append("}");
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,6 @@
import com.intellij.psi.PsiJavaFile;
import com.intellij.psi.PsiMethod;
import com.intellij.psi.PsiModifier;
import com.intellij.psi.PsiType;
import com.intellij.psi.codeStyle.CodeStyleManager;
import com.intellij.psi.util.PropertyUtilBase;
import com.intellij.psi.util.PsiUtil;
Expand All @@ -33,54 +32,51 @@ public GenerationResult generate() {
if (targetClass == null || BUILDER_CLASS_NAME.equals(targetClass.getName())) {
return NO_RESULT;
}
var psiElementFactory = generatorParams.psi().factory();
var builderClass = findOrCreateBuilderClass(targetClass);
var builderType = psiElementFactory.createTypeFromText(BUILDER_CLASS_NAME, targetClass);

if (!targetClass.isRecord()) {
var constructor = generateTargetConstructor(targetClass, builderType);
var constructor = generateTargetConstructor(targetClass, builderClass);
addMethod(targetClass, null, constructor, true);
}

var newBuilderMethod = generateStaticBuilderMethod(targetClass, builderType);
var newBuilderMethod = generateStaticBuilderMethod(targetClass, builderClass);
addMethod(targetClass, null, newBuilderMethod, false);

// toBuilder method
var options = generatorParams.options();
if (options.contains(JavaInnerBuilderOption.WITH_TO_BUILDER_METHOD)) {
var toBuilderMethod = generateToBuilderMethod(targetClass, builderType,
var toBuilderMethod = generateToBuilderMethod(targetClass, builderClass,
generatorParams.psi().selectedFields());
addMethod(targetClass, null, toBuilderMethod, true);
}

var params = BuilderClassParams.builder()
.targetClass(targetClass)
.builderClass(builderClass)
.builderType(builderType)
.build();
var result = generatorFactory.createBuilderClassGenerator(generatorParams, params).generate();
generationResult.merge(result);
var codeStyleManager = generatorParams.psi().codeStyleManager();
generationResult.when(ANNOTATIONS_ADDED, () -> codeStyleManager.shortenClassReferences(targetClass));
generationResult.when(IMPORTS_ADDED, () -> codeStyleManager.removeRedundantImports((PsiJavaFile) file));
CodeStyleManager.getInstance(generatorParams.project()).reformat(builderClass);
CodeStyleManager.getInstance(generatorParams.project()).reformat(builderClass.psiClass());
return generationResult;
}

private PsiMethod generateToBuilderMethod(PsiClass targetClass,
PsiType builderType,
BuilderClass builderClass,
Collection<PsiFieldMember> fields) {
var targetModifierList = Objects.requireNonNull(targetClass.getModifierList());
boolean isPublic = targetModifierList.hasModifierProperty(PsiModifier.PUBLIC);
var toBuilderMethod = new StringBuilder()
.append(isPublic ? PsiModifier.PUBLIC : EMPTY)
.append(isPublic ? SPACE : EMPTY)
.append(builderType.getPresentableText())
.append(builderClass.builderType().getPresentableText())
.append(SPACE)
.append(TO_BUILDER_NAME)
.append("() {")
.append("var builder = new ")
.append(builderType.getPresentableText())
.append(builderClass.builderType().getPresentableText())
.append("();");
for (var member : fields) {
var field = member.getElement();
Expand All @@ -97,9 +93,10 @@ private PsiMethod generateToBuilderMethod(PsiClass targetClass,
return psiElementFactory.createMethodFromText(toBuilderMethod.toString(), targetClass);
}

private PsiMethod generateStaticBuilderMethod(PsiClass targetClass, PsiType builderType) {
private PsiMethod generateStaticBuilderMethod(PsiClass targetClass, BuilderClass builderClass) {
var psiElementFactory = generatorParams.psi().factory();
var newBuilderMethod = psiElementFactory.createMethod(BUILDER_METHOD_NAME, builderType);
var methodName = Utils.buildBuilderMethodName(builderClass);
var newBuilderMethod = psiElementFactory.createMethodFromText(methodName, targetClass);
PsiUtil.setModifierProperty(newBuilderMethod, PsiModifier.STATIC, true);
PsiUtil.setModifierProperty(newBuilderMethod, PsiModifier.PUBLIC, true);

Expand All @@ -108,18 +105,18 @@ private PsiMethod generateStaticBuilderMethod(PsiClass targetClass, PsiType buil
existingMethod = newBuilderMethod;
var newBuilderMethodBody = Objects.requireNonNull(existingMethod.getBody());
var newStatement = psiElementFactory.createStatementFromText(String.format(
"return new %s();", builderType.getPresentableText()), newBuilderMethod);
"return new %s();", builderClass.builderClassName().instanceClassName()), newBuilderMethod);
newBuilderMethodBody.add(newStatement);
}
return existingMethod;
}

private PsiMethod generateTargetConstructor(final PsiClass targetClass, final PsiType builderType) {
private PsiMethod generateTargetConstructor(final PsiClass targetClass, BuilderClass builderClass) {
var constructor = new StringBuilder()
.append("private ")
.append(targetClass.getName())
.append("(")
.append(builderType.getPresentableText())
.append(builderClass.builderType().getPresentableText())
.append(" builder) {");

for (var member : generatorParams.psi().selectedFields()) {
Expand Down Expand Up @@ -151,19 +148,22 @@ private PsiMethod generateTargetConstructor(final PsiClass targetClass, final Ps
}

@NotNull
private PsiClass findOrCreateBuilderClass(final PsiClass targetClass) {
var builderClass = targetClass.findInnerClassByName(BUILDER_CLASS_NAME, false);
if (builderClass == null) {
return (PsiClass) targetClass.add(createBuilderClass(targetClass));
private BuilderClass findOrCreateBuilderClass(final PsiClass targetClass) {
var builderClassName = Utils.buildClassName(BUILDER_CLASS_NAME, targetClass);
var psiClass = targetClass.findInnerClassByName(BUILDER_CLASS_NAME, false);
if (psiClass == null) {
psiClass = (PsiClass) targetClass.add(createBuilderClass(targetClass, builderClassName.className()));
}

return builderClass;
var psiElementFactory = generatorParams.psi().factory();
var builderType = psiElementFactory.createTypeFromText(builderClassName.className(), targetClass);
return new BuilderClass(psiClass, builderType, builderClassName, Utils.isGenericType(builderType));
}

@NotNull
private PsiClass createBuilderClass(final PsiClass targetClass) {
String classDef = "public static final class " +
BUILDER_CLASS_NAME +
private PsiClass createBuilderClass(final PsiClass targetClass, String builderClassName) {
var classDef = "public static final class " +
builderClassName +
" {}" +
System.lineSeparator();
return generatorParams.psi().factory().createClassFromText(classDef, targetClass)
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@

import com.intellij.openapi.editor.Editor;
import com.intellij.psi.PsiClass;
import com.intellij.psi.PsiClassType;
import com.intellij.psi.PsiField;
import com.intellij.psi.PsiFile;
import com.intellij.psi.PsiMethod;
Expand All @@ -19,6 +20,8 @@
import java.util.Optional;
import java.util.function.Predicate;

import static com.github.junkfactory.innerbuilder.generators.AbstractGenerator.BUILDER_METHOD_NAME;

public class Utils {
@NonNls
static final String JAVA_DOT_LANG = "java.lang.";
Expand Down Expand Up @@ -147,9 +150,49 @@ public static List<String> stringToList(String str) {
.toList();
}

public static String parseType(String text) {
var parenthesisIndex = text.indexOf('(');
return parenthesisIndex == -1 ? text : text.substring(0, parenthesisIndex);
public static boolean isGenericType(PsiType psiType) {
// Check if the type is a PsiClassType
if (psiType instanceof PsiClassType classType) {
// Check if it has type parameters
return classType.getParameters().length > 0;
}
return false;
}

public static BuilderClassName buildClassName(String className, PsiClass targetClass) {
var builderClassName = new StringBuilder(className);
var typeParameters = targetClass.getTypeParameters();
if (typeParameters.length > 0) {
builderClassName.append('<');
for (int i = 0, l = typeParameters.length; i < l; i++) {
var typeParameter = typeParameters[i];
builderClassName.append(typeParameter.getName());
if (i < l - 1) {
builderClassName.append(", ");
}
}
builderClassName.append('>');
return new BuilderClassName(builderClassName.toString(), "%s<>".formatted(className));
}
return new BuilderClassName(builderClassName.toString(), className);
}

public static String buildBuilderMethodName(BuilderClass builderClass) {
var psiClassType = (PsiClassType) builderClass.builderType();
if (builderClass.genericType()) {
var typeParameters = psiClassType.getParameters();
var typeParameterNames = new StringBuilder();
for (int i = 0, l = typeParameters.length; i < l; i++) {
var typeParameter = typeParameters[i];
typeParameterNames.append(typeParameter.getPresentableText());
if (i < l - 1) {
typeParameterNames.append(", ");
}
}
return String.format("<%s> %s %s(){}", typeParameterNames,
psiClassType.getPresentableText(), BUILDER_METHOD_NAME);
}
return String.format("%s %s(){}", psiClassType.getPresentableText(), BUILDER_METHOD_NAME);
}

}
Loading