Moving or renaming individual classes is a no-brainer in today’s IDEs. But what if you want to rename thousands of classes in hundreds of packages? What if you want to reorganize a large codebase into a new module structure?
Refactobot takes a codebase and some transformations, and applies them to the codebase fully automatically, adapting all references. Transformations may include:
-
Changing class and package names
-
Moving classes between packages and modules (in a multi-module build)
- Automated and fast
-
Transforming a codebase just means running a script, and processes thousands of classes within seconds. This means you can reliably reproduce the transformation as many times as needed, as the codebase is still undergoing changes.
- Fully preserves formatting, whitespace, and comments
-
Refactobot only touches references to classes, when they are renamed or moved. All other code and formatting remains as it is.
- Declarative and powerful transformation language
-
Transformations are expressed declaratively in a Kotlin-based DSL. From simple move commands, to regex-based matching, to custom scripting (in Kotlin), refactoring scripts usually are just a few lines of simple code.
- Structure101 class map import
-
If you use Structure 101 for planning your refactoring, you can directly export a class map and apply it using Refactobot.
- Battle-tested
-
Refactobot was used successfully to establish a completely revamped module structure for a 200kloc codebase, which involved moving almost every class to a new place, and introduce uniform naming and packaging conventions. Of course, every project is different, so it may need some tweaking to work
- Hackable
-
Since every codebase is different, some adaptation may be needed in some cases. Refactobot is easily extensible due to its modular design. On the other hand, do not expect a polished product-like experience. This is for developers!
Uniformly rename classes, to establish or change some naming conventions:
val refactobot = Refactobot.configure {
refactor {
renameFile("""(.*)DaoBean\.java""" to "(.*)DaoImpl.java")
}
}
refactobot.run("/path/to/codebase")
Fix the base package:
val oldBasePackage = "org/example/legacybasepackage/"
val newBasePackage = "org/example/newbasepackage/"
val refactobot = Refactobot.configure {
refactor {
if (this.path.startsWith(oldBasePackage)) {
this.path = newBasePackage + this.path.removePrefix(oldBasePackage)
}
}
}
refactobot.run("/path/to/codebase")
If you have a map with fully qualified class names, you can apply it like this:
val refactobot = Refactobot.configure {
refactor {
applyClassMap(mapOf(
"org.example.codebase.SomeClass" to "org.example.codebase.NewClassName",
"org.example.codebase.SomeOtherClass" to "org.example.codebase.ShinyNewOtherClassName"
))
}
}
refactobot.run("/path/to/codebase")