Skip to content
Merged
Show file tree
Hide file tree
Changes from 1 commit
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
Original file line number Diff line number Diff line change
Expand Up @@ -228,9 +228,11 @@ private static class LombokValueToRecordVisitor extends JavaIsoVisitor<Execution
.contextSensitive()
.build();


private static final String TO_STRING_MEMBER_LINE_PATTERN = "\"%s=\" + %s +";
private static final String TO_STRING_MEMBER_DELIMITER = "\", \" +\n";
private static final String STANDARD_GETTER_PREFIX = "get";
private static final String BOOLEAN_GETTER_PREFIX = "is";

private final @Nullable Boolean useExactToString;
private final Map<String, Set<String>> recordTypeToMembers;
Expand All @@ -249,9 +251,16 @@ public J.MethodInvocation visitMethodInvocation(J.MethodInvocation method, Execu
}

J.Identifier methodName = methodInvocation.getName();
String simpleName = methodName.getSimpleName();

// Don't convert is* methods as they will be provided by the generated methods
if (simpleName.startsWith(BOOLEAN_GETTER_PREFIX)) {
return methodInvocation;
}

return methodInvocation
.withName(methodName
.withSimpleName(getterMethodNameToFluentMethodName(methodName.getSimpleName()))
.withSimpleName(getterMethodNameToFluentMethodName(simpleName))
);
}

Expand All @@ -264,18 +273,22 @@ public J.MemberReference visitMemberReference(J.MemberReference memberRef, Execu
String classFqn = ((JavaType.Class) containing.getType()).getFullyQualifiedName();
J.Identifier reference = memberReference.getReference();
String methodName = reference.getSimpleName();
String newSimpleName = getterMethodNameToFluentMethodName(methodName);
if (recordTypeToMembers.containsKey(classFqn) &&
methodName.startsWith(STANDARD_GETTER_PREFIX) &&
recordTypeToMembers.get(classFqn).contains(newSimpleName)) {

JavaType.Method methodType = memberReference.getMethodType();
if (methodType != null) {
methodType = methodType.withName(newSimpleName);

if (recordTypeToMembers.containsKey(classFqn)) {
// Handle get* methods
if (methodName.startsWith(STANDARD_GETTER_PREFIX)) {
String newSimpleName = getterMethodNameToFluentMethodName(methodName);
if (recordTypeToMembers.get(classFqn).contains(newSimpleName)) {
JavaType.Method methodType = memberReference.getMethodType();
if (methodType != null) {
methodType = methodType.withName(newSimpleName);
}
return memberReference
.withReference(reference.withSimpleName(newSimpleName))
.withMethodType(methodType);
}
}
return memberReference
.withReference(reference.withSimpleName(newSimpleName))
.withMethodType(methodType);
// Don't convert is* method references as they will be provided by generated methods
}
}
return memberReference;
Expand All @@ -295,9 +308,20 @@ private boolean isMethodInvocationOnRecordTypeClassMember(J.MethodInvocation met
String methodName = methodInvocation.getName().getSimpleName();
String classFqn = classType.getFullyQualifiedName();

return recordTypeToMembers.containsKey(classFqn) &&
methodName.startsWith(STANDARD_GETTER_PREFIX) &&
recordTypeToMembers.get(classFqn).contains(getterMethodNameToFluentMethodName(methodName));
if (!recordTypeToMembers.containsKey(classFqn)) {
return false;
}

// Handle both get* and is* methods
if (methodName.startsWith(STANDARD_GETTER_PREFIX)) {
return recordTypeToMembers.get(classFqn).contains(getterMethodNameToFluentMethodName(methodName));
} else if (methodName.startsWith(BOOLEAN_GETTER_PREFIX)) {
// For is* methods, check if the field exists (e.g., isBar -> bar)
String fieldName = booleanGetterMethodNameToFluentMethodName(methodName);
return recordTypeToMembers.get(classFqn).contains(fieldName);
}

return false;
}

private static boolean isClassExpression(@Nullable Expression expression) {
Expand All @@ -318,6 +342,24 @@ private static String getterMethodNameToFluentMethodName(String methodName) {
return fluentMethodName.toString();
}

private static String booleanGetterMethodNameToFluentMethodName(String methodName) {
if (!methodName.startsWith(BOOLEAN_GETTER_PREFIX)) {
return methodName;
}

StringBuilder fluentMethodName = new StringBuilder(
methodName.substring(BOOLEAN_GETTER_PREFIX.length()));

if (fluentMethodName.length() == 0) {
return "";
}

char firstMemberChar = fluentMethodName.charAt(0);
fluentMethodName.setCharAt(0, Character.toLowerCase(firstMemberChar));

return fluentMethodName.toString();
}

private static List<Statement> mapToConstructorArguments(List<J.VariableDeclarations> memberVariables) {
return memberVariables
.stream()
Expand All @@ -338,6 +380,43 @@ private J.ClassDeclaration addExactToStringMethod(J.ClassDeclaration classDeclar
memberVariablesToString(getMemberVariableNames(memberVariables))));
}

private J.ClassDeclaration addBooleanGetterMethods(J.ClassDeclaration classDeclaration,
List<J.VariableDeclarations> memberVariables) {
List<String> booleanGetters = new ArrayList<>();
for (J.VariableDeclarations varDecl : memberVariables) {
JavaType type = varDecl.getType();
if (type != null && isBooleanType(type)) {
for (J.VariableDeclarations.NamedVariable var : varDecl.getVariables()) {
String fieldName = var.getSimpleName();
String capitalizedFieldName = Character.toUpperCase(fieldName.charAt(0)) + fieldName.substring(1);
String returnType = type instanceof JavaType.Primitive ? "boolean" : "Boolean";
booleanGetters.add("public " + returnType + " is" + capitalizedFieldName + "() { return " + fieldName + "; }");
}
}
}
if (!booleanGetters.isEmpty()) {
String allMethods = String.join("\n", booleanGetters);
JavaTemplate template = JavaTemplate
.builder(allMethods)
.contextSensitive()
.build();
return classDeclaration.withBody(template
.apply(new Cursor(getCursor(), classDeclaration.getBody()),
classDeclaration.getBody().getCoordinates().lastStatement()));
}
return classDeclaration;
}

private boolean isBooleanType(JavaType type) {
if (type instanceof JavaType.Primitive) {
return type == JavaType.Primitive.Boolean;
} else if (type instanceof JavaType.Class) {
String fqn = ((JavaType.Class) type).getFullyQualifiedName();
return "java.lang.Boolean".equals(fqn);
}
return false;
}

private static String memberVariablesToString(Set<String> memberVariables) {
return memberVariables
.stream()
Expand Down Expand Up @@ -390,6 +469,9 @@ public J.ClassDeclaration visitClassDeclaration(J.ClassDeclaration cd, Execution
classDeclaration = addExactToStringMethod(classDeclaration, memberVariables);
}

// Add is* methods for boolean fields
classDeclaration = addBooleanGetterMethods(classDeclaration, memberVariables);

return maybeAutoFormat(cd, classDeclaration, ctx);
}
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -273,6 +273,96 @@ public record A(
);
}

@Issue("https://github.com/openrewrite/rewrite-migrate-java/issues/812")
@Test
void booleanFieldWithIsGetter() {
//language=java
rewriteRun(
java(
"""
import lombok.Value;

@Value
public class Foo {
boolean bar;
}
""",
"""
public record Foo(
boolean bar) {
public boolean isBar() {
return bar;
}
}
"""
),
java(
"""
public class Baz {
public void baz(Foo foo) {
foo.isBar();
}
}
"""
)
);
}

@Issue("https://github.com/openrewrite/rewrite-migrate-java/issues/812")
@Test
void multipleBooleanFields() {
//language=java
rewriteRun(
s -> s.typeValidationOptions(TypeValidation.none()),
java(
"""
import lombok.Value;

@Value
public class Config {
boolean enabled;
Boolean active;
String name;
}
""",
"""
public record Config(
boolean enabled,
Boolean active,
String name) {
public boolean isEnabled() {
return enabled;
}

public Boolean isActive() {
return active;
}
}
"""
),
java(
"""
public class ConfigUser {
public void useConfig(Config config) {
if (config.isEnabled() && config.isActive()) {
System.out.println(config.getName());
}
}
}
""",
"""
public class ConfigUser {
public void useConfig(Config config) {
if (config.isEnabled() && config.isActive()) {
System.out.println(config.name());
}
}
}
"""
)
);
}

@Issue("https://github.com/openrewrite/rewrite-migrate-java/issues/449")
@Test
void methodReferences() {
Expand Down
Loading