Last active
March 2, 2024 09:18
-
-
Save Bit-Flipper/696262963a7c2985fb23442783695a76 to your computer and use it in GitHub Desktop.
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
package dev.bitflippers; | |
import com.fasterxml.jackson.annotation.JsonIgnore; | |
import com.fasterxml.jackson.annotation.JsonProperty; | |
import lombok.EqualsAndHashCode; | |
import lombok.Getter; | |
import lombok.Setter; | |
import lombok.ToString; | |
import org.openrewrite.Cursor; | |
import org.openrewrite.ExecutionContext; | |
import org.openrewrite.Option; | |
import org.openrewrite.ScanningRecipe; | |
import org.openrewrite.SourceFile; | |
import org.openrewrite.Tree; | |
import org.openrewrite.TreeVisitor; | |
import org.openrewrite.internal.lang.Nullable; | |
import org.openrewrite.java.ChangeType; | |
import org.openrewrite.java.JavaIsoVisitor; | |
import org.openrewrite.java.tree.J; | |
import java.nio.file.Path; | |
import java.util.Collection; | |
import java.util.Set; | |
import java.util.UUID; | |
@Getter | |
@ToString | |
@EqualsAndHashCode(callSuper = false) | |
public class DuplicateClass extends ScanningRecipe<DuplicateClass.Accumulator> { | |
@JsonIgnore | |
private static final String NEW_CLASS_PREFIX = "CopyOf"; | |
@Option(displayName = "Package", | |
description = "Name of the package with the class to be duplicated", | |
example = "dev.bitflippers") | |
String packageName; | |
@Option(displayName = "Class Name", | |
description = "Name of the class to be duplicated", | |
example = "ExampleClass") | |
String simpleClassName; | |
public DuplicateClass(@JsonProperty("packageName") String packageName, @JsonProperty("simpleClassName") String simpleClassName) { | |
this.packageName = packageName; | |
this.simpleClassName = simpleClassName; | |
} | |
private String getNewSimpleClassName() { | |
return NEW_CLASS_PREFIX + simpleClassName; | |
} | |
private String getFullyQualifiedClassName() { | |
return packageName + "." + simpleClassName; | |
} | |
private String getNewFullyQualifiedClassName() { | |
return packageName + "." + getNewSimpleClassName(); | |
} | |
private String getClassPath() { | |
return getFullyQualifiedClassName().replace(".", "/"); | |
} | |
private String getNewClassPath() { | |
return getNewFullyQualifiedClassName().replace(".", "/"); | |
} | |
@Override | |
public String getDisplayName() { | |
return "Duplicate Java Class"; | |
} | |
@Override | |
public String getDescription() { | |
return "Creates a copy of a given Java class with a new name."; | |
} | |
@Getter | |
@Setter | |
static class Accumulator { | |
SourceFile sourceFileToDuplicate; | |
boolean shouldCreate = true; | |
} | |
@Override | |
public Accumulator getInitialValue(ExecutionContext ctx) { | |
return new Accumulator(); | |
} | |
@Override | |
public TreeVisitor<?, ExecutionContext> getScanner(Accumulator acc) { | |
return new TreeVisitor<>() { | |
@Override | |
public @Nullable Tree visit(@Nullable Tree tree, ExecutionContext executionContext, Cursor parent) { | |
SourceFile sourceFile = (SourceFile) tree; | |
String path = sourceFile.getSourcePath().toString(); | |
if (path.contains(getNewClassPath())) { | |
acc.setShouldCreate(false); | |
} else if (acc.getSourceFileToDuplicate() == null && path.contains(getClassPath())) { | |
acc.setSourceFileToDuplicate(sourceFile); | |
} | |
return sourceFile; | |
} | |
}; | |
} | |
@Override | |
public Collection<? extends SourceFile> generate(Accumulator acc, ExecutionContext ctx) { | |
var sourceFile = acc.getSourceFileToDuplicate(); | |
if (!acc.shouldCreate || sourceFile == null) { | |
return Set.of(); | |
} | |
var oldPath = sourceFile.getSourcePath(); | |
var newPath = Path.of(oldPath.toString().replace(getClassPath(), getNewClassPath())); | |
SourceFile newSourceFile = sourceFile.withSourcePath(newPath) | |
.withId(UUID.randomUUID()); | |
return Set.of(newSourceFile); | |
} | |
@Override | |
public TreeVisitor<?, ExecutionContext> getVisitor(Accumulator acc) { | |
return new JavaIsoVisitor<>(){ | |
@Override | |
public boolean isAcceptable(SourceFile sourceFile, ExecutionContext executionContext) { | |
return sourceFile.getSourcePath().toString().contains(getNewClassPath()); | |
} | |
@Override | |
public J.ClassDeclaration visitClassDeclaration(J.ClassDeclaration classDecl, ExecutionContext executionContext) { | |
var changeTypeRecipe = new ChangeType(getFullyQualifiedClassName(), | |
getNewFullyQualifiedClassName(), | |
false); | |
doAfterVisit(changeTypeRecipe.getVisitor()); | |
return super.visitClassDeclaration(classDecl, executionContext); | |
} | |
}; | |
} | |
} |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment