Skip to content

Commit dd19cb4

Browse files
Add support for kotlin in ChangeSpringPropertyKey and avoid regenerative changes in annotations. (#899)
* Add support for kotlin in ChangeSpringPropertyKey and avoid regenerative changes in annotations. * bot comments * Use immediate returns instead of assign before return * Strip regex suffixes that look like version patterns --------- Co-authored-by: Tim te Beek <[email protected]>
1 parent 2f04883 commit dd19cb4

File tree

2 files changed

+128
-26
lines changed

2 files changed

+128
-26
lines changed

src/main/java/org/openrewrite/java/spring/ChangeSpringPropertyKey.java

Lines changed: 62 additions & 23 deletions
Original file line numberDiff line numberDiff line change
@@ -20,11 +20,13 @@
2020
import org.jspecify.annotations.Nullable;
2121
import org.openrewrite.*;
2222
import org.openrewrite.internal.ListUtils;
23+
import org.openrewrite.internal.StringUtils;
2324
import org.openrewrite.java.AnnotationMatcher;
2425
import org.openrewrite.java.JavaIsoVisitor;
2526
import org.openrewrite.java.search.UsesType;
2627
import org.openrewrite.java.tree.J;
2728
import org.openrewrite.java.tree.JavaSourceFile;
29+
import org.openrewrite.kotlin.tree.K;
2830
import org.openrewrite.properties.search.FindProperties;
2931
import org.openrewrite.properties.tree.Properties;
3032
import org.openrewrite.yaml.ChangePropertyKey;
@@ -133,6 +135,9 @@ public J.Annotation visitAnnotation(J.Annotation annotation, ExecutionContext ct
133135
if (literal.getValue() instanceof String) {
134136
String value = (String) literal.getValue();
135137
if (value.contains(oldPropertyKey)) {
138+
if (newPropertyKey.contains(oldPropertyKey) && value.contains(newPropertyKey)) {
139+
return arg;
140+
}
136141
Pattern pattern = Pattern.compile("\\$\\{(" + quote(oldPropertyKey) + exceptRegex() + "(?:\\.[^.}:]+)*)(((?:\\\\.|[^}])*)\\})");
137142
Matcher matcher = pattern.matcher(value);
138143
int idx = 0;
@@ -157,8 +162,17 @@ public J.Annotation visitAnnotation(J.Annotation annotation, ExecutionContext ct
157162
}
158163
}
159164
}
160-
arg = literal.withValue(newValue)
161-
.withValueSource("\"" + newValue.replace("\\", "\\\\") + "\"");
165+
int leadingBackslashes = 0;
166+
for (int i = 0; i < newValue.length(); i++) {
167+
if (newValue.charAt(i) == '\\') {
168+
leadingBackslashes++;
169+
} else {
170+
break;
171+
}
172+
}
173+
174+
return literal.withValue(newValue)
175+
.withValueSource("\"" + StringUtils.repeat("\\", leadingBackslashes) + newValue.substring(leadingBackslashes).replace("\\", "\\\\") + "\"");
162176
}
163177
}
164178
}
@@ -170,14 +184,28 @@ public J.Annotation visitAnnotation(J.Annotation annotation, ExecutionContext ct
170184
} else if (CONDITIONAL_ON_PROPERTY_MATCHER.matches(annotation)) {
171185
if (a.getArguments() != null) {
172186
a = a.withArguments(ListUtils.map(a.getArguments(), arg -> {
173-
if (arg instanceof J.Assignment &&
174-
"name".equals(((J.Identifier) ((J.Assignment) arg).getVariable()).getSimpleName()) &&
175-
((J.Assignment) arg).getAssignment() instanceof J.Literal) {
176-
J.Assignment assignment = (J.Assignment) arg;
177-
J.Literal literal = (J.Literal) assignment.getAssignment();
178-
J.Literal newLiteral = changePropertyInLiteral(literal);
179-
if (newLiteral != literal) {
180-
arg = assignment.withAssignment(newLiteral);
187+
if (arg instanceof J.Assignment && "name".equals(((J.Identifier) ((J.Assignment) arg).getVariable()).getSimpleName())) {
188+
if (((J.Assignment) arg).getAssignment() instanceof J.Literal) {
189+
J.Assignment assignment = (J.Assignment) arg;
190+
J.Literal literal = (J.Literal) assignment.getAssignment();
191+
J.Literal newLiteral = changePropertyInLiteral(literal);
192+
if (newLiteral != literal) {
193+
return assignment.withAssignment(newLiteral);
194+
}
195+
}
196+
if (((J.Assignment) arg).getAssignment() instanceof K.ListLiteral) {
197+
J.Assignment assignment = (J.Assignment) arg;
198+
K.ListLiteral listLiteral = (K.ListLiteral) assignment.getAssignment();
199+
return assignment.withAssignment(listLiteral.withElements(ListUtils.map(listLiteral.getElements(), element -> {
200+
if (element instanceof J.Literal) {
201+
J.Literal literal = (J.Literal) element;
202+
J.Literal newLiteral = changePropertyInLiteral(literal);
203+
if (newLiteral != literal) {
204+
return newLiteral;
205+
}
206+
}
207+
return element;
208+
})));
181209
}
182210
}
183211
return arg;
@@ -187,12 +215,8 @@ public J.Annotation visitAnnotation(J.Annotation annotation, ExecutionContext ct
187215
a = a.withArguments(ListUtils.map(a.getArguments(), arg -> {
188216
if (arg instanceof J.NewArray) {
189217
J.NewArray array = (J.NewArray) arg;
190-
return array.withInitializer(ListUtils.map(array.getInitializer(), property -> {
191-
if (property instanceof J.Literal) {
192-
property = changePropertyInLiteral((J.Literal) property);
193-
}
194-
return property;
195-
}));
218+
return array.withInitializer(ListUtils.map(array.getInitializer(),
219+
property -> property instanceof J.Literal ? changePropertyInLiteral((J.Literal) property) : property));
196220
}
197221
if (arg instanceof J.Literal) {
198222
return changePropertyInLiteral((J.Literal) arg);
@@ -204,19 +228,31 @@ public J.Annotation visitAnnotation(J.Annotation annotation, ExecutionContext ct
204228
J.Literal literal = (J.Literal) assignment.getAssignment();
205229
J.Literal newLiteral = changePropertyInLiteral(literal);
206230
if (newLiteral != literal) {
207-
arg = assignment.withAssignment(newLiteral);
231+
return assignment.withAssignment(newLiteral);
208232
}
209233
} else if (assignment.getAssignment() instanceof J.NewArray) {
210234
J.NewArray array = (J.NewArray) assignment.getAssignment();
211-
arg = assignment.withAssignment(array.withInitializer(ListUtils.map(array.getInitializer(), property -> {
212-
if (property instanceof J.Literal) {
213-
property = changePropertyInLiteral((J.Literal) property);
214-
}
215-
return property;
216-
})));
235+
return assignment.withAssignment(array.withInitializer(ListUtils.map(array.getInitializer(),
236+
property -> property instanceof J.Literal ? changePropertyInLiteral((J.Literal) property) : property)));
217237
}
218238

219239
}
240+
if (arg instanceof J.Lambda) {
241+
J.Lambda lambda = (J.Lambda) arg;
242+
return lambda.withBody(lambda.getBody() instanceof J.Block ?
243+
((J.Block) lambda.getBody()).withStatements(ListUtils.map(((J.Block) lambda.getBody()).getStatements(), statement -> {
244+
if (statement instanceof K.ExpressionStatement &&
245+
((K.ExpressionStatement) statement).getExpression() instanceof J.Literal) {
246+
J.Literal literal = (J.Literal) ((K.ExpressionStatement) statement).getExpression();
247+
J.Literal newLiteral = changePropertyInLiteral(literal);
248+
if (newLiteral != literal) {
249+
return ((K.ExpressionStatement) statement).withExpression(newLiteral);
250+
}
251+
}
252+
return statement;
253+
})) :
254+
lambda.getBody());
255+
}
220256
return arg;
221257
}));
222258
}
@@ -229,6 +265,9 @@ private J.Literal changePropertyInLiteral(J.Literal literal) {
229265
return literal;
230266
}
231267
String value = literal.getValue().toString();
268+
if (newPropertyKey.contains(oldPropertyKey) && value.contains(newPropertyKey)) {
269+
return literal;
270+
}
232271
Pattern pattern = Pattern.compile("^" + quote(oldPropertyKey) + exceptRegex());
233272
Matcher matcher = pattern.matcher(value);
234273
if (matcher.find()) {

src/test/java/org/openrewrite/java/spring/ChangeSpringPropertyKeyTest.java

Lines changed: 66 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -21,11 +21,14 @@
2121
import org.openrewrite.InMemoryExecutionContext;
2222
import org.openrewrite.Issue;
2323
import org.openrewrite.java.JavaParser;
24+
import org.openrewrite.kotlin.KotlinParser;
2425
import org.openrewrite.test.RewriteTest;
2526

2627
import java.util.List;
2728

29+
import static java.util.Collections.emptyList;
2830
import static org.openrewrite.java.Assertions.*;
31+
import static org.openrewrite.kotlin.Assertions.kotlin;
2932
import static org.openrewrite.properties.Assertions.properties;
3033
import static org.openrewrite.yaml.Assertions.yaml;
3134

@@ -170,7 +173,7 @@ void except() {
170173
}
171174

172175
@Test
173-
void avoidRegenerativeChanges() {
176+
void avoidRegenerativeChangesInYaml() {
174177
rewriteRun(
175178
spec -> spec.recipe(new ChangeSpringPropertyKey("logging.file", "logging.file.name", null)),
176179
mavenProject("project",
@@ -283,7 +286,7 @@ void loggingFileSubpropertiesYaml() {
283286
void changeValueAnnotation() {
284287
rewriteRun(
285288
spec -> spec.recipe(new ChangeSpringPropertyKey("server.servlet-path", "server.servlet.path", List.of("foo")))
286-
.parser(JavaParser.fromJavaVersion().classpathFromResources(new InMemoryExecutionContext(), "spring-beans-5.+")),
289+
.parser(JavaParser.fromJavaVersion().classpathFromResources(new InMemoryExecutionContext(), "spring-beans-5")),
287290
java(
288291
"""
289292
import org.springframework.beans.factory.annotation.Value;
@@ -322,7 +325,7 @@ class MyConfiguration {
322325
void changeConditionalOnPropertyAnnotation() {
323326
rewriteRun(
324327
spec -> spec.recipe(new ChangeSpringPropertyKey("foo", "bar", List.of("baz")))
325-
.parser(JavaParser.fromJavaVersion().classpathFromResources(new InMemoryExecutionContext(), "spring-boot-autoconfigure-2.+")),
328+
.parser(JavaParser.fromJavaVersion().classpathFromResources(new InMemoryExecutionContext(), "spring-boot-autoconfigure-2")),
326329
java(
327330
"""
328331
import org.springframework.boot.autoconfigure.condition.ConditionalOnProperty;
@@ -475,4 +478,64 @@ class SomeTest {}
475478
)
476479
);
477480
}
481+
482+
@Test
483+
void avoidRegenerativeChangesInAnnotation() {
484+
rewriteRun(
485+
spec -> spec
486+
.recipe(
487+
new ChangeSpringPropertyKey("spring.data.redis.ssl", "spring.data.redis.ssl.enabled", emptyList()))
488+
.parser(JavaParser.fromJavaVersion().classpathFromResources(new InMemoryExecutionContext(), "spring-beans-5", "spring-boot-test-3.2", "spring-boot-autoconfigure-2")),
489+
java(
490+
"""
491+
import org.springframework.beans.factory.annotation.Value;
492+
import org.springframework.boot.test.context.SpringBootTest;
493+
import org.springframework.boot.autoconfigure.condition.ConditionalOnProperty;
494+
495+
@SpringBootTest({ "spring.data.redis.ssl.enabled=true" })
496+
@ConditionalOnProperty(name = "spring.data.redis.ssl.enabled", havingValue = "true")
497+
class MyConfiguration {
498+
@Value("${spring.data.redis.ssl.enabled:false}")
499+
private boolean redisSslEnabled;
500+
}
501+
"""
502+
)
503+
);
504+
}
505+
506+
@Test
507+
void supportKotlinAnnotations() {
508+
rewriteRun(
509+
spec -> spec
510+
.recipe(
511+
new ChangeSpringPropertyKey("spring.data.redis.ssl.enabled", "some.other.key.enabled", emptyList()))
512+
.parser(KotlinParser.builder().classpathFromResources(new InMemoryExecutionContext(), "spring-beans-5", "spring-boot-test-3.2", "spring-boot-autoconfigure-2")),
513+
kotlin(
514+
"""
515+
import org.springframework.beans.factory.annotation.Value
516+
import org.springframework.boot.test.context.SpringBootTest
517+
import org.springframework.boot.autoconfigure.condition.ConditionalOnProperty
518+
519+
@SpringBootTest({ "spring.data.redis.ssl.enabled=true" })
520+
@ConditionalOnProperty(name = ["spring.data.redis.ssl.enabled"], havingValue = "true")
521+
class MyConfiguration {
522+
@Value("\\${spring.data.redis.ssl.enabled:false}")
523+
private val useSSl: Boolean = false
524+
}
525+
""",
526+
"""
527+
import org.springframework.beans.factory.annotation.Value
528+
import org.springframework.boot.test.context.SpringBootTest
529+
import org.springframework.boot.autoconfigure.condition.ConditionalOnProperty
530+
531+
@SpringBootTest({ "some.other.key.enabled=true" })
532+
@ConditionalOnProperty(name = ["some.other.key.enabled"], havingValue = "true")
533+
class MyConfiguration {
534+
@Value("\\${some.other.key.enabled:false}")
535+
private val useSSl: Boolean = false
536+
}
537+
"""
538+
)
539+
);
540+
}
478541
}

0 commit comments

Comments
 (0)