Skip to content

Commit 37c2d76

Browse files
authored
AddProperty add options for controlling where new properties get inserted (#7275)
* AddProperty add options for controlling where new properties get inserted * Stupid javadoc
1 parent d934b7b commit 37c2d76

6 files changed

Lines changed: 412 additions & 117 deletions

File tree

rewrite-gradle/src/main/java/org/openrewrite/gradle/AddProperty.java

Lines changed: 4 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -124,7 +124,8 @@ public Tree visit(@Nullable Tree tree, ExecutionContext ctx) {
124124
sourceFile :
125125
new ChangePropertyValue(key, value, null, false, null)
126126
.getVisitor().visitNonNull(sourceFile, ctx);
127-
return new org.openrewrite.properties.AddProperty(key, value, null, null, null)
127+
return org.openrewrite.properties.AddProperty.builder()
128+
.property(key).value(value).build()
128129
.getVisitor()
129130
.visitNonNull(t, ctx);
130131
}
@@ -133,7 +134,8 @@ public Tree visit(@Nullable Tree tree, ExecutionContext ctx) {
133134
sourceFile :
134135
new ChangePropertyValue(key, value, null, false, null)
135136
.getVisitor().visitNonNull(sourceFile, ctx);
136-
return new org.openrewrite.properties.AddProperty(key, value, null, null, null)
137+
return org.openrewrite.properties.AddProperty.builder()
138+
.property(key).value(value).build()
137139
.getVisitor()
138140
.visitNonNull(t, ctx);
139141
}

rewrite-properties/build.gradle.kts

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,10 @@ plugins {
22
id("org.openrewrite.build.language-library")
33
}
44

5+
tasks.withType<Javadoc>().configureEach {
6+
exclude("**/AddProperty**")
7+
}
8+
59
dependencies {
610
api(project(":rewrite-core"))
711
api("org.jetbrains:annotations:latest.release")

rewrite-properties/src/main/java/org/openrewrite/properties/AddProperty.java

Lines changed: 78 additions & 16 deletions
Original file line numberDiff line numberDiff line change
@@ -15,6 +15,9 @@
1515
*/
1616
package org.openrewrite.properties;
1717

18+
import com.fasterxml.jackson.annotation.JsonCreator;
19+
import lombok.AllArgsConstructor;
20+
import lombok.Builder;
1821
import lombok.EqualsAndHashCode;
1922
import lombok.Value;
2023
import org.jspecify.annotations.Nullable;
@@ -34,9 +37,12 @@
3437
import static java.util.Collections.singletonList;
3538
import static java.util.stream.Collectors.toList;
3639
import static org.openrewrite.Tree.randomId;
40+
import static org.openrewrite.internal.StringUtils.isBlank;
3741

3842
@Value
43+
@AllArgsConstructor(onConstructor_ = {@JsonCreator})
3944
@EqualsAndHashCode(callSuper = false)
45+
@Builder
4046
public class AddProperty extends Recipe {
4147

4248
@Option(displayName = "Property key",
@@ -70,17 +76,40 @@ public class AddProperty extends Recipe {
7076
@Nullable
7177
Boolean orderedInsertion;
7278

79+
@Option(displayName = "Insert mode",
80+
description = "Choose an insertion point relative to an existing property. Default is `Last`. " +
81+
"Takes precedence over `orderedInsertion`. " +
82+
"If the referenced property does not exist, falls back to default behavior.",
83+
valid = {"Before", "After", "Last"},
84+
required = false)
85+
@Nullable
86+
InsertMode insertMode;
87+
88+
@Option(displayName = "Insert property",
89+
description = "The key of an existing property to use as the reference point for the insert mode. " +
90+
"Required when `insertMode` is `Before` or `After`.",
91+
required = false,
92+
example = "server.port")
93+
@Nullable
94+
String insertProperty;
95+
96+
public enum InsertMode {Before, After, Last}
97+
7398
String displayName = "Add a new property";
7499

75100
String description = "Adds a new property to a property file. " +
76-
"Attempts to place the new property in alphabetical order by the property keys. " +
77-
"Whitespace before and after the `=` must be included in the property and value.";
101+
"Attempts to place the new property in alphabetical order by the property keys. " +
102+
"Whitespace before and after the `=` must be included in the property and value.";
78103

79104
@Override
80105
public Validated<Object> validate() {
81106
return Validated.none()
82107
.and(Validated.required("property", property))
83-
.and(Validated.required("value", value));
108+
.and(Validated.required("value", value))
109+
.and(Validated.test("insertProperty",
110+
"Insert property must be provided when `insertMode` is `Before` or `After`.",
111+
insertProperty,
112+
s -> insertMode == null || insertMode == InsertMode.Last || !isBlank(s)));
84113
}
85114

86115
@Override
@@ -104,7 +133,7 @@ public Properties.File visitFile(Properties.File file, ExecutionContext ctx) {
104133

105134

106135
List<Properties.Content> newContents;
107-
if(StringUtils.isBlank(comment)) {
136+
if (StringUtils.isBlank(comment)) {
108137
newContents = singletonList(entry);
109138
} else {
110139
newContents = Arrays.asList(
@@ -113,28 +142,44 @@ public Properties.File visitFile(Properties.File file, ExecutionContext ctx) {
113142
"\n",
114143
Markers.EMPTY,
115144
Properties.Comment.Delimiter.HASH_TAG,
116-
" " + comment.trim()),
145+
" " + comment.trim()),
117146
entry);
118147
}
119148

120-
List<Properties.Content> contentList = new ArrayList<>(p.getContent().size() + 1);
121-
if (orderedInsertion == null || orderedInsertion) {
122-
int insertionIndex = sortedInsertionIndex(entry, p.getContent());
123-
contentList.addAll(p.getContent().subList(0, insertionIndex));
124-
contentList.addAll(newContents);
125-
contentList.addAll(p.getContent().subList(insertionIndex, p.getContent().size()));
149+
List<Properties.Content> contentList = new ArrayList<>(p.getContent().size() + newContents.size());
150+
boolean inserted = false;
151+
152+
if (insertMode != null && insertMode != InsertMode.Last && !isBlank(insertProperty)) {
153+
int refIndex = findEntryIndex(p.getContent(), p, insertProperty);
154+
if (refIndex >= 0) {
155+
int insertIndex = insertMode == InsertMode.Before ?
156+
findCommentBlockStart(p.getContent(), refIndex) :
157+
refIndex + 1;
158+
contentList.addAll(p.getContent().subList(0, insertIndex));
159+
contentList.addAll(newContents);
160+
contentList.addAll(p.getContent().subList(insertIndex, p.getContent().size()));
161+
inserted = true;
162+
}
126163
}
127-
else {
128-
contentList.addAll(p.getContent());
129-
contentList.addAll(newContents);
164+
165+
if (!inserted) {
166+
if (orderedInsertion == null || orderedInsertion) {
167+
int insertionIndex = sortedInsertionIndex(entry, p.getContent());
168+
contentList.addAll(p.getContent().subList(0, insertionIndex));
169+
contentList.addAll(newContents);
170+
contentList.addAll(p.getContent().subList(insertionIndex, p.getContent().size()));
171+
} else {
172+
contentList.addAll(p.getContent());
173+
contentList.addAll(newContents);
174+
}
130175
}
131176

132177

133178
// First entry in the file does not need a newline, but every other entry does
134179
contentList = ListUtils.map(contentList, (i, c) -> {
135-
if(i == 0) {
180+
if (i == 0) {
136181
return (Properties.Content) c.withPrefix("");
137-
} else if(!c.getPrefix().contains("\n")) {
182+
} else if (!c.getPrefix().contains("\n")) {
138183
return (Properties.Content) c.withPrefix("\n" + c.getPrefix());
139184
}
140185
return c;
@@ -145,6 +190,23 @@ public Properties.File visitFile(Properties.File file, ExecutionContext ctx) {
145190
};
146191
}
147192

193+
private static int findEntryIndex(List<Properties.Content> contentList, Properties.File file, String referenceKey) {
194+
Set<Properties.Entry> matches = FindProperties.find(file, referenceKey, false);
195+
if (matches.isEmpty()) {
196+
return -1;
197+
}
198+
Properties.Entry matched = matches.iterator().next();
199+
return contentList.indexOf(matched);
200+
}
201+
202+
private static int findCommentBlockStart(List<Properties.Content> contentList, int entryIndex) {
203+
int start = entryIndex;
204+
while (start > 0 && contentList.get(start - 1) instanceof Properties.Comment) {
205+
start--;
206+
}
207+
return start;
208+
}
209+
148210
private static int sortedInsertionIndex(Properties.Entry entry, List<Properties.Content> contentsList) {
149211
if (contentsList.isEmpty()) {
150212
return 0;

rewrite-properties/src/main/resources/META-INF/rewrite/examples.yml

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -22,6 +22,8 @@ examples:
2222
- 'null'
2323
- 'null'
2424
- 'null'
25+
- 'null'
26+
- 'null'
2527
sources:
2628
- before: |
2729
management=true

rewrite-properties/src/main/resources/META-INF/rewrite/recipes.csv

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
ecosystem,packageName,name,displayName,description,recipeCount,category1,category2,options
2-
maven,org.openrewrite:rewrite-properties,org.openrewrite.properties.AddProperty,Add a new property,Adds a new property to a property file. Attempts to place the new property in alphabetical order by the property keys. Whitespace before and after the `=` must be included in the property and value.,1,,Properties,"[{""name"":""property"",""type"":""String"",""displayName"":""Property key"",""description"":""The property key to add."",""example"":""management.metrics.enable.process.files"",""required"":true},{""name"":""value"",""type"":""String"",""displayName"":""Property value"",""description"":""The value of the new property key."",""example"":""newPropValue"",""required"":true},{""name"":""comment"",""type"":""String"",""displayName"":""Optional comment to be prepended to the property"",""description"":""A comment that will be added to the new property."",""example"":""This is a comment""},{""name"":""delimiter"",""type"":""String"",""displayName"":""Optional delimiter"",""description"":""Property entries support different delimiters (`=`, `:`, or whitespace). The default value is `=` unless provided the delimiter of the new property entry."",""example"":"":""},{""name"":""orderedInsertion"",""type"":""Boolean"",""displayName"":""Ordered property insertion"",""description"":""Whether to attempt adding the property in an order following alphabetic sorting. The default value is `true`."",""example"":""false""}]"
2+
maven,org.openrewrite:rewrite-properties,org.openrewrite.properties.AddProperty,Add a new property,Adds a new property to a property file. Attempts to place the new property in alphabetical order by the property keys. Whitespace before and after the `=` must be included in the property and value.,1,,Properties,"[{""name"":""property"",""type"":""String"",""displayName"":""Property key"",""description"":""The property key to add."",""example"":""management.metrics.enable.process.files"",""required"":true},{""name"":""value"",""type"":""String"",""displayName"":""Property value"",""description"":""The value of the new property key."",""example"":""newPropValue"",""required"":true},{""name"":""comment"",""type"":""String"",""displayName"":""Optional comment to be prepended to the property"",""description"":""A comment that will be added to the new property."",""example"":""This is a comment""},{""name"":""delimiter"",""type"":""String"",""displayName"":""Optional delimiter"",""description"":""Property entries support different delimiters (`=`, `:`, or whitespace). The default value is `=` unless provided the delimiter of the new property entry."",""example"":"":""},{""name"":""orderedInsertion"",""type"":""Boolean"",""displayName"":""Ordered property insertion"",""description"":""Whether to attempt adding the property in an order following alphabetic sorting. The default value is `true`."",""example"":""false""},{""name"":""insertMode"",""type"":""org.openrewrite.properties.AddProperty$InsertMode"",""displayName"":""Insert mode"",""description"":""Choose an insertion point relative to an existing property. Default is `Last`. Takes precedence over `orderedInsertion`. If the referenced property does not exist, falls back to default behavior."",""valid"":[""Before"",""After"",""Last""]},{""name"":""insertProperty"",""type"":""String"",""displayName"":""Insert property"",""description"":""The key of an existing property to use as the reference point for the insert mode. Required when `insertMode` is `Before` or `After`."",""example"":""server.port""}]"
33
maven,org.openrewrite:rewrite-properties,org.openrewrite.properties.AddPropertyComment,Add comment before property key,"Add a new comment before a property key if not already present, optionally commenting out the property.",1,,Properties,"[{""name"":""propertyKey"",""type"":""String"",""displayName"":""Property key"",""description"":""The name of the property to add comment."",""example"":""management.metrics.binders"",""required"":true},{""name"":""comment"",""type"":""String"",""displayName"":""Comment"",""description"":""The comment to be added."",""example"":""comment"",""required"":true},{""name"":""commentOutProperty"",""type"":""Boolean"",""displayName"":""Comment out property"",""description"":""If true, property will be commented out."",""example"":""true""}]"
44
maven,org.openrewrite:rewrite-properties,org.openrewrite.properties.CopyValue,Copy property value,"Copies a property value from one key to another. The existing key/value pair remains unaffected by this change. If the destination key already exists, its value will be replaced. By default, creates the destination key if it does not exist.",1,,Properties,"[{""name"":""oldPropertyKey"",""type"":""String"",""displayName"":""Old property key"",""description"":""The property key to copy the value from. Supports glob patterns."",""example"":""app.source.property"",""required"":true},{""name"":""oldFilePath"",""type"":""String"",""displayName"":""Old file path"",""description"":""The file path to the properties file to copy the value from. If `null` then the value will be copied from any properties file it appears within."",""example"":""src/main/resources/application.properties""},{""name"":""newPropertyKey"",""type"":""String"",""displayName"":""New property key"",""description"":""The property key to copy the value to."",""example"":""app.destination.property"",""required"":true},{""name"":""newFilePath"",""type"":""String"",""displayName"":""New file path"",""description"":""The file path to the properties file to copy the value to. If `null` then the value will be copied only into the same file it was found in."",""example"":""src/main/resources/application.properties""},{""name"":""createNewKeys"",""type"":""Boolean"",""displayName"":""Create new keys"",""description"":""When the destination key does _not_ already exist, create it. Default is `true`.""},{""name"":""relaxedBinding"",""type"":""Boolean"",""displayName"":""Use relaxed binding"",""description"":""Whether to match the `oldPropertyKey` using [relaxed binding](https://docs.spring.io/spring-boot/docs/2.5.6/reference/html/features.html#features.external-config.typesafe-configuration-properties.relaxed-binding) rules. Default is `true`. Set to `false` to use exact matching.""}]"
55
maven,org.openrewrite:rewrite-properties,org.openrewrite.properties.ChangePropertyKey,Change property key,Change a property key leaving the value intact.,1,,Properties,"[{""name"":""oldPropertyKey"",""type"":""String"",""displayName"":""Old property key"",""description"":""The property key to rename."",""example"":""management.metrics.binders.files.enabled"",""required"":true},{""name"":""newPropertyKey"",""type"":""String"",""displayName"":""New property key"",""description"":""The new name for the key identified by `oldPropertyKey`."",""example"":""management.metrics.enable.process.files"",""required"":true},{""name"":""relaxedBinding"",""type"":""Boolean"",""displayName"":""Use relaxed binding"",""description"":""Whether to match the `oldPropertyKey` using [relaxed binding](https://docs.spring.io/spring-boot/docs/2.5.6/reference/html/features.html#features.external-config.typesafe-configuration-properties.relaxed-binding) rules. Default is `true`. Set to `false` to use exact matching.""},{""name"":""regex"",""type"":""Boolean"",""displayName"":""Regex"",""description"":""Default false. If enabled, `oldPropertyKey` will be interpreted as a Regular Expression, and capture group contents will be available in `newPropertyKey`""}]"

0 commit comments

Comments
 (0)