Skip to content

Instantly share code, notes, and snippets.

@andrewgilmartin
Last active June 6, 2022 16:39
Show Gist options
  • Save andrewgilmartin/9894388f3d24ab97a959a17598322a6a to your computer and use it in GitHub Desktop.
Save andrewgilmartin/9894388f3d24ab97a959a17598322a6a to your computer and use it in GitHub Desktop.
package org.openrewrite.starter;
import org.openrewrite.ExecutionContext;
import org.openrewrite.Recipe;
import org.openrewrite.SourceFile;
import static org.openrewrite.Tree.randomId;
import org.openrewrite.java.JavaIsoVisitor;
import org.openrewrite.java.tree.J;
import org.openrewrite.java.tree.JLeftPadded;
import org.openrewrite.java.tree.JavaType;
import org.openrewrite.java.tree.Statement;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
/**
* This visitor will transform the format of final static variable names to the Java convention
* of terms separated by underscores and in all uppercase. For example, "aSimpleName" becomes
* "A_SIMPLE_NAME".
*
* This implementation draws from org.openrewrite.java.ChangeFieldName and the Slack discussion
* at https://rewriteoss.slack.com/archives/C01A843MWG5/p1654200119901319
*/
public class UppercaseStaticFinalVariables extends Recipe {
@Override
public String getDisplayName() {
return "Format static final variable names.";
}
@Override
public String getDescription() {
return "Format static final variable names to the Java convention of terms separated by underscores and in all uppercase.";
}
@Override
protected List<SourceFile> visit(List<SourceFile> sources, ExecutionContext context) {
sources = super.visit(sources, context);
// look for class variable declarations needing updating
List<Discovery> discoveries = new ArrayList<>();
for (SourceFile source : sources) {
new DiscoveryVisitor(discoveries).visit(source, context);
}
// update the discovered variables declaration and uses
List<SourceFile> updatedSources = new ArrayList<>();
UpdateVisitor updateVisitor = new UpdateVisitor(discoveries);
for (SourceFile source : sources) {
SourceFile updatedSource = (SourceFile) updateVisitor.visit(source, context);
updatedSources.add(updatedSource);
}
return updatedSources;
}
private static boolean isUppercaseSnakeFormattedName(String name) {
return name.matches("^[A-Z][A-Z0-9_]*$");
}
private static String toUppercaseSnakeFormattedName(String name) {
// handle transition from lowercase to uppercase or number, eg aB -> a_B and a0 -> a_0
name = name.replaceAll("([a-z])([A-Z0-9])", "$1_$2");
// handle transition from number to letter, eg 0A -> 0_A and 0a -> 0_a
name = name.replaceAll("([0-9])([a-zA-Z])", "$1_$2");
name = name.toUpperCase();
return name;
}
private static class Discovery {
JavaType.Variable variable;
String newName;
public Discovery(JavaType.Variable variable, String newName) {
this.variable = variable;
this.newName = newName;
}
}
private static class DiscoveryVisitor extends JavaIsoVisitor<ExecutionContext> {
private final List<Discovery> discoveries;
public DiscoveryVisitor(List<Discovery> discoveries) {
this.discoveries = discoveries;
}
@Override
public J.ClassDeclaration visitClassDeclaration(J.ClassDeclaration classDeclaration, ExecutionContext executionContext) {
classDeclaration = super.visitClassDeclaration(classDeclaration, executionContext);
if (classDeclaration.getType() == null) {
return classDeclaration;
}
for (Statement statement : classDeclaration.getBody().getStatements()) {
if (statement instanceof J.VariableDeclarations) {
// eg String a, b c;
J.VariableDeclarations variables = (J.VariableDeclarations) statement;
if (variables.hasModifier(J.Modifier.Type.Static) && variables.hasModifier(J.Modifier.Type.Final)) {
// eg static final String a, b c;
for (J.VariableDeclarations.NamedVariable variable : variables.getVariables()) {
if (!isUppercaseSnakeFormattedName(variable.getSimpleName())) {
Discovery discovery = new Discovery(
variable.getVariableType(),
toUppercaseSnakeFormattedName(variable.getSimpleName())
);
discoveries.add(discovery);
}
}
}
}
}
return classDeclaration;
}
}
private static class UpdateVisitor extends JavaIsoVisitor<ExecutionContext> {
private final Map<JavaType.Variable, Discovery> variableToDiscovery = new HashMap<>();
public UpdateVisitor(List<Discovery> discoveries) {
for (Discovery discovery : discoveries) {
variableToDiscovery.put(discovery.variable, discovery);
}
}
@Override
public J.VariableDeclarations.NamedVariable visitVariable(J.VariableDeclarations.NamedVariable variable, ExecutionContext context) {
variable = super.visitVariable(variable, context);
Discovery discovery = variableToDiscovery.get(variable.getVariableType());
if (discovery != null) {
J.VariableDeclarations.NamedVariable original = variable;
variable = original.withName(original.getName().withSimpleName(discovery.newName));
if (original.getPadding().getInitializer() != null) {
variable = variable.getPadding().withInitializer(
visitLeftPadded(
variable.getPadding().getInitializer(),
JLeftPadded.Location.VARIABLE_INITIALIZER,
context
));
}
}
return variable;
}
@Override
public J.Identifier visitIdentifier(J.Identifier identifier, ExecutionContext context) {
identifier = super.visitIdentifier(identifier, context);
Discovery discovery = variableToDiscovery.get(identifier.getFieldType());
if (discovery != null) {
identifier = identifier
.withId(randomId())
.withSimpleName(discovery.newName)
.withFieldType(identifier.getFieldType().withName(discovery.newName));
}
return identifier;
}
}
}
// END
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment