org.openrewrite.text.AppendToTextFile Maven / Gradle / Ivy
Show all versions of rewrite-core Show documentation
/*
* Copyright 2022 the original author or authors.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* https://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.openrewrite.text;
import lombok.EqualsAndHashCode;
import lombok.Value;
import org.jspecify.annotations.Nullable;
import org.openrewrite.*;
import java.nio.file.Path;
import java.nio.file.Paths;
import java.util.Collection;
import java.util.Collections;
import java.util.concurrent.atomic.AtomicBoolean;
import static java.util.Objects.requireNonNull;
@Value
@EqualsAndHashCode(callSuper = false)
public class AppendToTextFile extends ScanningRecipe {
@Option(displayName = "Relative File Name",
description = "File name, using a relative path. If a non-plaintext file already exists at this location, then this recipe will do nothing.",
example = "foo/bar/baz.txt")
String relativeFileName;
@Option(displayName = "Content",
description = "Multiline text content to be appended to the file.",
example = "Some text.")
String content;
@Option(displayName = "Preamble",
description = "If a new file is created, this content will be included at the beginning.",
example = "# File generated by OpenRewrite #",
required = false)
@Nullable String preamble;
@Option(displayName = "Append newline",
description = "Print a newline automatically after the content (and preamble). Default true.",
required = false)
@Nullable Boolean appendNewline;
@Option(displayName = "Existing file strategy",
description = "Determines behavior if a file exists at this location prior to Rewrite execution.\n\n"
+ "- `Continue`: append new content to existing file contents. If existing file is not plaintext, recipe does nothing.\n"
+ "- `Replace`: remove existing content from file.\n"
+ "- `Leave`: *(default)* do nothing. Existing file is fully preserved.\n\n"
+ "Note: this only affects the first interaction with the specified file per Rewrite execution.\n"
+ "Subsequent instances of this recipe in the same Rewrite execution will always append.",
valid = {"Continue", "Replace", "Leave"},
required = false)
@Nullable Strategy existingFileStrategy;
public enum Strategy {Continue, Replace, Leave}
@Override
public String getDisplayName() {
return "Append to text file";
}
@Override
public String getDescription() {
return "Appends or replaces content of an existing plain text file, or creates a new one if it doesn't already exist. " +
"Please note that this recipes requires existing plain text files' format to be successfully parsable by OpenRewrite. " +
"If a file is left unchanged, it might be parsed as a `Quark` rather than plain text. In such case, use the `plainTextMask` option. " +
"See the [Gradle](https://docs.openrewrite.org/reference/gradle-plugin-configuration#configuring-the-rewrite-dsl) or " +
"[Maven](https://openrewrite.github.io/rewrite-maven-plugin/run-mojo.html#plainTextMasks) plugin configuration page.";
}
@Override
public int maxCycles() {
return 1;
}
@Override
public AtomicBoolean getInitialValue(ExecutionContext ctx) {
return new AtomicBoolean(false);
}
@Override
public TreeVisitor, ExecutionContext> getScanner(AtomicBoolean fileExists) {
return new TreeVisitor() {
@Override
public Tree visit(@Nullable Tree tree, ExecutionContext ctx) {
SourceFile sourceFile = (SourceFile) requireNonNull(tree);
if (!fileExists.get() && sourceFile.getSourcePath().toString().equals(Paths.get(relativeFileName).toString())) {
fileExists.set(true);
}
return sourceFile;
}
};
}
@Override
public Collection generate(AtomicBoolean fileExists, Collection generatedInThisCycle, ExecutionContext ctx) {
String maybeNewline = !Boolean.FALSE.equals(appendNewline) ? "\n" : "";
String content = this.content + maybeNewline;
String preamble = this.preamble != null ? this.preamble + maybeNewline : "";
boolean exists = fileExists.get();
Path path = Paths.get(relativeFileName);
if (!exists) {
for (SourceFile generated : generatedInThisCycle) {
if (generated.getSourcePath().toString().equals(path.toString())) {
exists = true;
break;
}
}
}
return exists ?
Collections.emptyList() :
Collections.singletonList(PlainText.builder()
.text(preamble + content)
.sourcePath(path)
.build());
}
@Override
public TreeVisitor, ExecutionContext> getVisitor(AtomicBoolean fileExists) {
return Preconditions.check(fileExists.get(), new TreeVisitor() {
@Override
public Tree visit(@Nullable Tree tree, ExecutionContext ctx) {
SourceFile sourceFile = (SourceFile) requireNonNull(tree);
if (sourceFile.getSourcePath().toString().equals(Paths.get(relativeFileName).toString())) {
String maybeNewline = !Boolean.FALSE.equals(appendNewline) ? "\n" : "";
String content = AppendToTextFile.this.content + maybeNewline;
String preamble = AppendToTextFile.this.preamble != null ? AppendToTextFile.this.preamble + maybeNewline : "";
PlainText existingPlainText = (PlainText) sourceFile;
switch (existingFileStrategy != null ? existingFileStrategy : Strategy.Leave) {
case Continue:
if (!maybeNewline.isEmpty() && !existingPlainText.getText().endsWith(maybeNewline)) {
content = maybeNewline + content;
}
return existingPlainText.withText(existingPlainText.getText() + content);
case Replace:
return existingPlainText.withText(preamble + content);
}
}
return sourceFile;
}
});
}
}