Last active
June 6, 2022 16:39
-
-
Save andrewgilmartin/9894388f3d24ab97a959a17598322a6a 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 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