A very good friend of mine,
Tomas Votruba created
Rector, a command line tool for instant upgrades and automated refactoring in PHP.
About three years ago, I mentioned creating the same tool for Java, hypothesizing that it would be incredibly lucrative as it seemed there were so many medium to large sized companies that used Java and had terrible legacy systems.
It took me three years to put fingertips to keyboard, but I have done it. ‘How on earth does that work?’ I hear you doubting Thomas's heckle.
The tool converts your code to Abstract Syntax Tree (AST) basically objects of code.
There are a list of rules that can be applied to the AST, each rule has one very specific task, for example “ConstantNameToUpperSnakeCaseRule” This rule will only find class constants and change the name to upper snake case, which is a common convention.
A group of rules are in a set, for example the “ConstantNameToUpperSnakeCaseRule” is part of the NamingSet, that way you can add many rules in one line in your config file.
Let’s have a look a the config file:
package com.example.config;
import com.example.RuleList;
import com.example.SetList;
import com.example.rules.RuleInterface;
import com.example.sets.SetInterface;
import java.util.List;
public class JavaUpgraderConfig implements JavaUpgraderConfigInterface {
@Override
public List addSets() {
return List.of(SetList.NAMING_SET);
}
@Override
public List addRules() {
return List.of(RuleList.DEAD_CODE);
}
}
The config file allows you to addSets or `addRules`, in this case we have added the naming set which is comprised of 5 rules (for now) and we have also added the dead code rule, which will remove any code that isn’t being used.
What does a rule look like I hear you ask?
Let’s have a look at the `ConstantNameToUpperSnakeCaseRule`
package com.example.rules.naming;
import com.example.model.UpgraderFileModel;
import com.example.rules.RuleInterface;
import com.github.javaparser.ast.body.FieldDeclaration;
public class ConstantNameToUpperSnakeCaseRule implements RuleInterface {
@Override
public void execute(UpgraderFileModel fileModel) {
fileModel.getAst().findAll(FieldDeclaration.class).forEach(fieldDeclaration -> {
if (fieldDeclaration.isFinal()) {
fieldDeclaration.getVariables().forEach(variable -> {
String oldName = variable.getNameAsString();
String newName = convertToUpperCase(oldName);
variable.setName(newName);
});
}
});
}
private String convertToUpperCase(String input) {
return input.toUpperCase();
}
@Override
public String getDescription() {
return "Converts constant names from snake_case to upper snake case";
}
@Override
public String getName() {
return "ConstantNameToUpperCaseConverter";
}
@Override
public String getBeforeState() {
return """
public class Example {
public static final int my_constant_value = 42;
}
""";
}
@Override
public String getAfterState() {
return """
public class Example {
public static final int MY_CONSTANT_VALUE = 42;
}
""";
}
}
All rules must implement the `RuleInterface` which requires us to have the following methods:
getName, getDescription, getBeforeState, getAfterState and execute.
The name allows us to single out a rule by name, the description tells us what the rule does, the beforeState and afterState tells us what effect the rule will have on the code, but also is there for testing purposes, to make sure that is exactly what the rule does and finally the execute method actions the change of the rule.
This is just the beginning, I’ll be creating sets to upgrade your code quality, ensure design pattern conventions and even migrate to different Java versions.
If you are interested in the JavaUpgrader and want to find out more, please visit
http://javaupgrader.com/ join the waiting list or message me directly if you have a specific question.