All Downloads are FREE. Search and download functionalities are using the official Maven repository.

org.openrewrite.staticanalysis.RenameLocalVariablesToCamelCase Maven / Gradle / Ivy

There is a newer version: 2.0.2
Show newest version
/*
 * Copyright 2024 the original author or authors.
 * 

* Licensed under the Moderne Source Available License (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at *

* https://docs.moderne.io/licensing/moderne-source-available-license *

* 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.staticanalysis; import org.openrewrite.*; import org.openrewrite.java.tree.J; import org.openrewrite.java.tree.JavaSourceFile; import org.openrewrite.java.tree.JavaType; import org.openrewrite.staticanalysis.csharp.CSharpFileChecker; import java.time.Duration; import java.util.Collections; import java.util.List; import java.util.Set; import static org.openrewrite.internal.NameCaseConvention.LOWER_CAMEL; /** * This recipe converts local variables and method parameters to camel case convention. * The recipe will not rename variables declared in for loop controls or catches with a single character. *
* The first character is set to lower case and existing capital letters are preserved. * Special characters that are allowed in java field names `$` and `_` are removed. * If a special character is removed the next valid alphanumeric will be capitalized. *
* Currently, unsupported: * - The recipe will not rename variables declared in a class. * - The recipe will not rename variables if the result already exists in a class or the result will be a java reserved keyword. */ public class RenameLocalVariablesToCamelCase extends Recipe { @Override public String getDisplayName() { return "Reformat local variable names to camelCase"; } @Override public String getDescription() { return "Reformat local variable and method parameter names to camelCase to comply with Java naming convention. " + "The recipe will not rename variables declared in for loop controls or catches with a single character. " + "The first character is set to lower case and existing capital letters are preserved. " + "Special characters that are allowed in java field names `$` and `_` are removed (unless the name starts with one). " + "If a special character is removed the next valid alphanumeric will be capitalized. " + "Currently, does not support renaming members of classes. " + "The recipe will not rename a variable if the result already exists in the class, conflicts with a java reserved keyword, or the result is blank."; } @Override public Set getTags() { return Collections.singleton("RSPEC-S117"); } @Override public Duration getEstimatedEffortPerOccurrence() { return Duration.ofMinutes(2); } @Override public TreeVisitor getVisitor() { return Preconditions.check(Preconditions.not(new CSharpFileChecker<>()), new RenameToCamelCase() { @Override protected boolean shouldRename(Set hasNameSet, J.VariableDeclarations.NamedVariable variable, String toName) { if (toName.isEmpty() || !Character.isAlphabetic(toName.charAt(0))) { return false; } return isAvailableIdentifier(toName, variable, hasNameSet); } @Override public J.VariableDeclarations visitVariableDeclarations(J.VariableDeclarations multiVariable, ExecutionContext ctx) { J.VariableDeclarations mv = super.visitVariableDeclarations(multiVariable, ctx); // the meaning of a local variable is “is contained in a method declaration body”. if (!isLocalVariable(mv)) { return mv; } List variables = mv.getVariables(); for (J.VariableDeclarations.NamedVariable v : variables) { String name = v.getSimpleName(); if (!LOWER_CAMEL.matches(name) && name.length() > 1) { renameVariable(v, LOWER_CAMEL.format(name)); } else { hasNameKey(computeKey(name, v)); } } return mv; } private boolean isLocalVariable(J.VariableDeclarations mv) { // The recipe will not rename variables declared in for loop controls or catches. if (!isInMethodDeclarationBody() || isDeclaredInForLoopControl() || isDeclaredInCatch() || isMethodArgument()) { return false; } // Ignore fields (aka "instance variable" or "class variable") for (J.VariableDeclarations.NamedVariable v : mv.getVariables()) { if (v.isField(getCursor())) { return false; } } return true; } private boolean isMethodArgument() { return getCursor().getParentTreeCursor() .getValue() instanceof J.MethodDeclaration; } private boolean isInMethodDeclarationBody() { return getCursor().dropParentUntil(p -> p instanceof J.MethodDeclaration || p instanceof J.ClassDeclaration || p instanceof J.NewClass || p == Cursor.ROOT_VALUE).getValue() instanceof J.MethodDeclaration; } private boolean isDeclaredInForLoopControl() { return getCursor().getParentTreeCursor() .getValue() instanceof J.ForLoop.Control; } private boolean isDeclaredInCatch() { Cursor parentScope = getCursorToParentScope(getCursor()); return parentScope.getValue() instanceof J.Try.Catch || parentScope.getValue() instanceof J.MultiCatch; } @Override public J.Identifier visitIdentifier(J.Identifier identifier, ExecutionContext ctx) { hasNameKey(computeKey(identifier.getSimpleName(), identifier)); return identifier; } /** * Returns either the current block or a J.Type that may create a reference to a variable. * I.E. for(int target = 0; target < N; target++) creates a new name scope for `target`. * The name scope in the next J.Block `{}` cannot create new variables with the name `target`. *

* J.* types that may only reference an existing name and do not create a new name scope are excluded. */ private Cursor getCursorToParentScope(Cursor cursor) { return cursor.dropParentUntil(is -> is instanceof J.ClassDeclaration || is instanceof J.Block || is instanceof J.MethodDeclaration || is instanceof J.ForLoop || is instanceof J.ForEachLoop || is instanceof J.ForLoop.Control || is instanceof J.Case || is instanceof J.Try || is instanceof J.Try.Catch || is instanceof J.MultiCatch || is instanceof J.Lambda || is instanceof JavaSourceFile ); } private boolean isAvailableIdentifier(String identifier, J context, Set hasNameSet) { if (hasNameSet.contains(identifier)) { return false; } JavaType.Variable fieldType = getFieldType(context); if (fieldType != null && fieldType.getOwner() != null) { if (hasNameSet.contains(fieldType.getOwner() + " " + identifier)) { return false; } if (fieldType.getOwner() instanceof JavaType.Method) { // Add all enclosing classes JavaType.FullyQualified declaringType = ((JavaType.Method) fieldType.getOwner()).getDeclaringType(); while (declaringType != null) { if (hasNameSet.contains(declaringType + " " + identifier)) { return false; } declaringType = declaringType.getOwningClass(); } } } return true; } }); } }





© 2015 - 2025 Weber Informatics LLC | Privacy Policy