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
Original file line number Diff line number Diff line change
Expand Up @@ -124,7 +124,8 @@ public Tree visit(@Nullable Tree tree, ExecutionContext ctx) {
sourceFile :
new ChangePropertyValue(key, value, null, false, null)
.getVisitor().visitNonNull(sourceFile, ctx);
return new org.openrewrite.properties.AddProperty(key, value, null, null, null)
return org.openrewrite.properties.AddProperty.builder()
.property(key).value(value).build()
.getVisitor()
.visitNonNull(t, ctx);
}
Expand All @@ -133,7 +134,8 @@ public Tree visit(@Nullable Tree tree, ExecutionContext ctx) {
sourceFile :
new ChangePropertyValue(key, value, null, false, null)
.getVisitor().visitNonNull(sourceFile, ctx);
return new org.openrewrite.properties.AddProperty(key, value, null, null, null)
return org.openrewrite.properties.AddProperty.builder()
.property(key).value(value).build()
.getVisitor()
.visitNonNull(t, ctx);
}
Expand Down
4 changes: 4 additions & 0 deletions rewrite-properties/build.gradle.kts
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,10 @@ plugins {
id("org.openrewrite.build.language-library")
}

tasks.withType<Javadoc>().configureEach {
exclude("**/AddProperty**")
}

dependencies {
api(project(":rewrite-core"))
api("org.jetbrains:annotations:latest.release")
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -15,6 +15,9 @@
*/
package org.openrewrite.properties;

import com.fasterxml.jackson.annotation.JsonCreator;
import lombok.AllArgsConstructor;
import lombok.Builder;
import lombok.EqualsAndHashCode;
import lombok.Value;
import org.jspecify.annotations.Nullable;
Expand All @@ -34,9 +37,12 @@
import static java.util.Collections.singletonList;
import static java.util.stream.Collectors.toList;
import static org.openrewrite.Tree.randomId;
import static org.openrewrite.internal.StringUtils.isBlank;

@Value
@AllArgsConstructor(onConstructor_ = {@JsonCreator})
@EqualsAndHashCode(callSuper = false)
@Builder
public class AddProperty extends Recipe {

@Option(displayName = "Property key",
Expand Down Expand Up @@ -70,17 +76,40 @@ public class AddProperty extends Recipe {
@Nullable
Boolean orderedInsertion;

@Option(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"},
required = false)
@Nullable
InsertMode insertMode;

@Option(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`.",
required = false,
example = "server.port")
@Nullable
String insertProperty;

public enum InsertMode {Before, After, Last}

String displayName = "Add a new property";

String description = "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.";
"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.";

@Override
public Validated<Object> validate() {
return Validated.none()
.and(Validated.required("property", property))
.and(Validated.required("value", value));
.and(Validated.required("value", value))
.and(Validated.test("insertProperty",
"Insert property must be provided when `insertMode` is `Before` or `After`.",
insertProperty,
s -> insertMode == null || insertMode == InsertMode.Last || !isBlank(s)));
}

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


List<Properties.Content> newContents;
if(StringUtils.isBlank(comment)) {
if (StringUtils.isBlank(comment)) {
newContents = singletonList(entry);
} else {
newContents = Arrays.asList(
Expand All @@ -113,28 +142,44 @@ public Properties.File visitFile(Properties.File file, ExecutionContext ctx) {
"\n",
Markers.EMPTY,
Properties.Comment.Delimiter.HASH_TAG,
" " + comment.trim()),
" " + comment.trim()),
entry);
}

List<Properties.Content> contentList = new ArrayList<>(p.getContent().size() + 1);
if (orderedInsertion == null || orderedInsertion) {
int insertionIndex = sortedInsertionIndex(entry, p.getContent());
contentList.addAll(p.getContent().subList(0, insertionIndex));
contentList.addAll(newContents);
contentList.addAll(p.getContent().subList(insertionIndex, p.getContent().size()));
List<Properties.Content> contentList = new ArrayList<>(p.getContent().size() + newContents.size());
boolean inserted = false;

if (insertMode != null && insertMode != InsertMode.Last && !isBlank(insertProperty)) {
int refIndex = findEntryIndex(p.getContent(), p, insertProperty);
if (refIndex >= 0) {
int insertIndex = insertMode == InsertMode.Before ?
findCommentBlockStart(p.getContent(), refIndex) :
refIndex + 1;
contentList.addAll(p.getContent().subList(0, insertIndex));
contentList.addAll(newContents);
contentList.addAll(p.getContent().subList(insertIndex, p.getContent().size()));
inserted = true;
}
}
else {
contentList.addAll(p.getContent());
contentList.addAll(newContents);

if (!inserted) {
if (orderedInsertion == null || orderedInsertion) {
int insertionIndex = sortedInsertionIndex(entry, p.getContent());
contentList.addAll(p.getContent().subList(0, insertionIndex));
contentList.addAll(newContents);
contentList.addAll(p.getContent().subList(insertionIndex, p.getContent().size()));
} else {
contentList.addAll(p.getContent());
contentList.addAll(newContents);
}
}


// First entry in the file does not need a newline, but every other entry does
contentList = ListUtils.map(contentList, (i, c) -> {
if(i == 0) {
if (i == 0) {
return (Properties.Content) c.withPrefix("");
} else if(!c.getPrefix().contains("\n")) {
} else if (!c.getPrefix().contains("\n")) {
return (Properties.Content) c.withPrefix("\n" + c.getPrefix());
}
return c;
Expand All @@ -145,6 +190,23 @@ public Properties.File visitFile(Properties.File file, ExecutionContext ctx) {
};
}

private static int findEntryIndex(List<Properties.Content> contentList, Properties.File file, String referenceKey) {
Set<Properties.Entry> matches = FindProperties.find(file, referenceKey, false);
if (matches.isEmpty()) {
return -1;
}
Properties.Entry matched = matches.iterator().next();
return contentList.indexOf(matched);
}

private static int findCommentBlockStart(List<Properties.Content> contentList, int entryIndex) {
int start = entryIndex;
while (start > 0 && contentList.get(start - 1) instanceof Properties.Comment) {
start--;
}
return start;
}

private static int sortedInsertionIndex(Properties.Entry entry, List<Properties.Content> contentsList) {
if (contentsList.isEmpty()) {
return 0;
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -22,6 +22,8 @@ examples:
- 'null'
- 'null'
- 'null'
- 'null'
- 'null'
sources:
- before: |
management=true
Expand Down
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
ecosystem,packageName,name,displayName,description,recipeCount,category1,category2,options
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""}]"
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""}]"
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""}]"
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.""}]"
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`""}]"
Expand Down
Loading