421 lines
11 KiB
Markdown
421 lines
11 KiB
Markdown
# Java LST Structure Reference
|
|
|
|
Complete reference for OpenRewrite's Java Lossless Semantic Tree (LST) structure.
|
|
|
|
## Overview
|
|
|
|
The Java LST represents Java code as a tree structure that preserves all formatting, comments, and whitespace. This allows transformations that maintain the original file's appearance.
|
|
|
|
## Type Hierarchy
|
|
|
|
```
|
|
org.openrewrite.java.tree.J
|
|
├── J.CompilationUnit (root of Java file)
|
|
├── J.ClassDeclaration (class definitions)
|
|
├── J.MethodDeclaration (method definitions)
|
|
├── J.MethodInvocation (method calls)
|
|
├── J.VariableDeclarations (variable declarations)
|
|
├── J.Block (code blocks)
|
|
├── J.If (if statements)
|
|
├── J.ForLoop (for loops)
|
|
├── J.WhileLoop (while loops)
|
|
├── J.Try (try-catch blocks)
|
|
├── J.Import (import statements)
|
|
├── J.Annotation (annotations)
|
|
├── J.Binary (binary operations: +, -, *, /, &&, ||, etc.)
|
|
├── J.Literal (primitive literals)
|
|
├── J.Identifier (variable/type names)
|
|
├── J.NewClass (object instantiation)
|
|
├── J.Return (return statements)
|
|
├── J.Assignment (assignments)
|
|
└── ... (many more)
|
|
```
|
|
|
|
## Core Types
|
|
|
|
### J.CompilationUnit
|
|
|
|
The root element of a Java source file.
|
|
|
|
```java
|
|
public interface CompilationUnit extends JavaSourceFile, J {
|
|
List<Import> getImports();
|
|
List<ClassDeclaration> getClasses();
|
|
Space getEof();
|
|
// ... other methods
|
|
}
|
|
```
|
|
|
|
**Usage:**
|
|
```java
|
|
@Override
|
|
public J.CompilationUnit visitCompilationUnit(J.CompilationUnit cu, ExecutionContext ctx) {
|
|
// Visit entire file
|
|
cu = super.visitCompilationUnit(cu, ctx);
|
|
|
|
// Access package declaration
|
|
String packageName = cu.getPackageDeclaration() != null ?
|
|
cu.getPackageDeclaration().getExpression().printTrimmed() : null;
|
|
|
|
// Access imports
|
|
List<J.Import> imports = cu.getImports();
|
|
|
|
// Access classes
|
|
List<J.ClassDeclaration> classes = cu.getClasses();
|
|
|
|
return cu;
|
|
}
|
|
```
|
|
|
|
---
|
|
|
|
### J.ClassDeclaration
|
|
|
|
Represents class, interface, enum, or record declarations.
|
|
|
|
```java
|
|
public interface ClassDeclaration extends Statement, TypedTree {
|
|
List<Annotation> getLeadingAnnotations();
|
|
List<Modifier> getModifiers();
|
|
Kind getKind(); // Class, Interface, Enum, Record, Annotation
|
|
Identifier getName();
|
|
@Nullable TypeParameters getTypeParameters();
|
|
@Nullable TypeTree getExtends();
|
|
@Nullable Container<TypeTree> getImplements();
|
|
Block getBody();
|
|
JavaType.FullyQualified getType();
|
|
}
|
|
```
|
|
|
|
**Usage:**
|
|
```java
|
|
@Override
|
|
public J.ClassDeclaration visitClassDeclaration(J.ClassDeclaration classDecl, ExecutionContext ctx) {
|
|
classDecl = super.visitClassDeclaration(classDecl, ctx);
|
|
|
|
// Get class name
|
|
String className = classDecl.getSimpleName();
|
|
|
|
// Get fully qualified name
|
|
if (classDecl.getType() != null) {
|
|
String fqn = classDecl.getType().getFullyQualifiedName();
|
|
}
|
|
|
|
// Check if interface
|
|
if (classDecl.getKind() == J.ClassDeclaration.Kind.Type.Interface) {
|
|
// ...
|
|
}
|
|
|
|
// Access methods
|
|
List<Statement> statements = classDecl.getBody().getStatements();
|
|
for (Statement statement : statements) {
|
|
if (statement instanceof J.MethodDeclaration) {
|
|
J.MethodDeclaration method = (J.MethodDeclaration) statement;
|
|
// Process method
|
|
}
|
|
}
|
|
|
|
return classDecl;
|
|
}
|
|
```
|
|
|
|
---
|
|
|
|
### J.MethodDeclaration
|
|
|
|
Represents method declarations.
|
|
|
|
```java
|
|
public interface MethodDeclaration extends Statement, TypedTree {
|
|
List<Annotation> getLeadingAnnotations();
|
|
List<Modifier> getModifiers();
|
|
@Nullable TypeParameters getTypeParameters();
|
|
@Nullable TypeTree getReturnTypeExpression();
|
|
Identifier getName();
|
|
List<Statement> getParameters();
|
|
@Nullable Container<NameTree> getThrows();
|
|
@Nullable Block getBody();
|
|
JavaType.Method getMethodType();
|
|
}
|
|
```
|
|
|
|
**Usage:**
|
|
```java
|
|
@Override
|
|
public J.MethodDeclaration visitMethodDeclaration(J.MethodDeclaration method, ExecutionContext ctx) {
|
|
method = super.visitMethodDeclaration(method, ctx);
|
|
|
|
// Get method name
|
|
String methodName = method.getSimpleName();
|
|
|
|
// Get parameters
|
|
List<Statement> params = method.getParameters();
|
|
|
|
// Get return type
|
|
TypeTree returnType = method.getReturnTypeExpression();
|
|
|
|
// Get method body
|
|
J.Block body = method.getBody();
|
|
|
|
// Check if method matches signature
|
|
if (method.getMethodType() != null &&
|
|
TypeUtils.isOfType(method.getMethodType(), "com.example.Type", "methodName")) {
|
|
// Method matches
|
|
}
|
|
|
|
return method;
|
|
}
|
|
```
|
|
|
|
---
|
|
|
|
### J.MethodInvocation
|
|
|
|
Represents method calls.
|
|
|
|
```java
|
|
public interface MethodInvocation extends Expression, TypedTree, MethodCall {
|
|
@Nullable Expression getSelect(); // Object being called on
|
|
Identifier getName();
|
|
List<Expression> getArguments();
|
|
JavaType.Method getMethodType();
|
|
}
|
|
```
|
|
|
|
**Usage:**
|
|
```java
|
|
@Override
|
|
public J.MethodInvocation visitMethodInvocation(J.MethodInvocation method, ExecutionContext ctx) {
|
|
method = super.visitMethodInvocation(method, ctx);
|
|
|
|
// Get method name
|
|
String methodName = method.getSimpleName();
|
|
|
|
// Get arguments
|
|
List<Expression> args = method.getArguments();
|
|
|
|
// Check if calling specific method
|
|
if (method.getMethodType() != null &&
|
|
TypeUtils.isOfType(method.getMethodType(), "java.lang.String", "equals")) {
|
|
// This is a String.equals() call
|
|
}
|
|
|
|
// Get select (object being called on)
|
|
Expression select = method.getSelect();
|
|
|
|
return method;
|
|
}
|
|
```
|
|
|
|
---
|
|
|
|
### J.VariableDeclarations
|
|
|
|
Represents variable declarations.
|
|
|
|
```java
|
|
public interface VariableDeclarations extends Statement, TypedTree {
|
|
List<Annotation> getLeadingAnnotations();
|
|
List<Modifier> getModifiers();
|
|
@Nullable TypeTree getTypeExpression();
|
|
List<NamedVariable> getVariables();
|
|
}
|
|
```
|
|
|
|
**Usage:**
|
|
```java
|
|
@Override
|
|
public J.VariableDeclarations visitVariableDeclarations(J.VariableDeclarations multiVariable, ExecutionContext ctx) {
|
|
multiVariable = super.visitVariableDeclarations(multiVariable, ctx);
|
|
|
|
// Get type
|
|
TypeTree type = multiVariable.getTypeExpression();
|
|
|
|
// Get all variables declared
|
|
for (J.VariableDeclarations.NamedVariable var : multiVariable.getVariables()) {
|
|
String varName = var.getSimpleName();
|
|
Expression initializer = var.getInitializer();
|
|
// Process variable
|
|
}
|
|
|
|
return multiVariable;
|
|
}
|
|
```
|
|
|
|
---
|
|
|
|
### J.Import
|
|
|
|
Represents import statements.
|
|
|
|
```java
|
|
public interface Import extends Statement {
|
|
boolean isStatic();
|
|
FieldAccess getQualid();
|
|
}
|
|
```
|
|
|
|
**Usage:**
|
|
```java
|
|
@Override
|
|
public J.Import visitImport(J.Import _import, ExecutionContext ctx) {
|
|
_import = super.visitImport(_import, ctx);
|
|
|
|
// Get fully qualified name
|
|
String fqn = _import.getQualid().printTrimmed();
|
|
|
|
// Check if static import
|
|
if (_import.isStatic()) {
|
|
// Static import
|
|
}
|
|
|
|
return _import;
|
|
}
|
|
```
|
|
|
|
---
|
|
|
|
### J.Annotation
|
|
|
|
Represents annotations.
|
|
|
|
```java
|
|
public interface Annotation extends Expression {
|
|
NameTree getAnnotationType();
|
|
@Nullable Container<Expression> getArguments();
|
|
}
|
|
```
|
|
|
|
**Usage:**
|
|
```java
|
|
@Override
|
|
public J.Annotation visitAnnotation(J.Annotation annotation, ExecutionContext ctx) {
|
|
annotation = super.visitAnnotation(annotation, ctx);
|
|
|
|
// Get annotation type
|
|
String annotationType = annotation.getAnnotationType().printTrimmed();
|
|
|
|
// Check specific annotation
|
|
if ("org.junit.Test".equals(annotationType)) {
|
|
// This is a @Test annotation
|
|
}
|
|
|
|
// Get arguments
|
|
if (annotation.getArguments() != null) {
|
|
List<Expression> args = annotation.getArguments().getElements();
|
|
}
|
|
|
|
return annotation;
|
|
}
|
|
```
|
|
|
|
---
|
|
|
|
## Common Patterns
|
|
|
|
### Type Checking
|
|
|
|
```java
|
|
// Check if method invocation is of specific type
|
|
if (method.getMethodType() != null &&
|
|
TypeUtils.isOfClassType(method.getMethodType().getDeclaringType(), "com.example.Class")) {
|
|
// Method is declared in com.example.Class
|
|
}
|
|
|
|
// Check method signature
|
|
if (TypeUtils.isOfType(method.getMethodType(), "com.example.Type", "methodName")) {
|
|
// Method matches
|
|
}
|
|
```
|
|
|
|
### Safe Value Access
|
|
|
|
```java
|
|
// Always check for null before accessing type information
|
|
if (classDecl.getType() != null) {
|
|
String fqn = classDecl.getType().getFullyQualifiedName();
|
|
}
|
|
|
|
// Check for null on optional elements
|
|
if (method.getBody() != null) {
|
|
List<Statement> statements = method.getBody().getStatements();
|
|
}
|
|
```
|
|
|
|
### Modifying LST Elements
|
|
|
|
```java
|
|
// Always use .withX() methods - never mutate
|
|
classDecl = classDecl.withName(classDecl.getName().withSimpleName("NewName"));
|
|
|
|
// Use ListUtils for list operations
|
|
classDecl = classDecl.withModifiers(
|
|
ListUtils.concat(classDecl.getModifiers(), newModifier)
|
|
);
|
|
|
|
// Remove from list
|
|
method = method.withArguments(
|
|
ListUtils.map(method.getArguments(), (i, arg) ->
|
|
i == indexToRemove ? null : arg
|
|
)
|
|
);
|
|
```
|
|
|
|
### Import Management
|
|
|
|
```java
|
|
// Add import if not present
|
|
maybeAddImport("java.util.List");
|
|
|
|
// Add static import
|
|
maybeAddImport("java.util.Collections", "emptyList");
|
|
|
|
// Remove import
|
|
maybeRemoveImport("old.package.Type");
|
|
```
|
|
|
|
### Visitor Chaining
|
|
|
|
```java
|
|
// Chain another visitor after this one
|
|
doAfterVisit(new SomeOtherRecipe().getVisitor());
|
|
```
|
|
|
|
## Visit Method Reference
|
|
|
|
Common visit methods to override:
|
|
|
|
| LST Element | Visit Method | Common Use |
|
|
|-------------|--------------|------------|
|
|
| `J.CompilationUnit` | `visitCompilationUnit()` | Entire file operations |
|
|
| `J.ClassDeclaration` | `visitClassDeclaration()` | Class modifications |
|
|
| `J.MethodDeclaration` | `visitMethodDeclaration()` | Method modifications |
|
|
| `J.MethodInvocation` | `visitMethodInvocation()` | Method call changes |
|
|
| `J.VariableDeclarations` | `visitVariableDeclarations()` | Variable operations |
|
|
| `J.Block` | `visitBlock()` | Code block operations |
|
|
| `J.If` | `visitIf()` | Conditional logic |
|
|
| `J.ForLoop` | `visitForLoop()` | Loop transformations |
|
|
| `J.Import` | `visitImport()` | Import management |
|
|
| `J.Annotation` | `visitAnnotation()` | Annotation operations |
|
|
| `J.Binary` | `visitBinary()` | Binary operations |
|
|
| `J.Literal` | `visitLiteral()` | Literal values |
|
|
| `J.Assignment` | `visitAssignment()` | Assignment operations |
|
|
| `J.Return` | `visitReturn()` | Return statements |
|
|
| `J.NewClass` | `visitNewClass()` | Object instantiation |
|
|
|
|
## Best Practices
|
|
|
|
1. **Always call super** - `super.visitX()` traverses the subtree
|
|
2. **Return modified copies** - Never mutate LST elements directly
|
|
3. **Use `.withX()` methods** - For all modifications
|
|
4. **Handle null cases** - Check for null before accessing type information
|
|
5. **Preserve formatting** - LST methods maintain formatting automatically
|
|
6. **Use ListUtils** - For all list operations (never mutate directly)
|
|
7. **Check types safely** - Use TypeUtils methods with null checks
|
|
|
|
## See Also
|
|
|
|
- `references/common-patterns.md` - Code patterns for common operations
|
|
- `references/troubleshooting-guide.md` - Solutions to common issues
|
|
- `templates/template-imperative-recipe.java` - Complete recipe template
|