# 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 getImports(); List 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 imports = cu.getImports(); // Access classes List classes = cu.getClasses(); return cu; } ``` --- ### J.ClassDeclaration Represents class, interface, enum, or record declarations. ```java public interface ClassDeclaration extends Statement, TypedTree { List getLeadingAnnotations(); List getModifiers(); Kind getKind(); // Class, Interface, Enum, Record, Annotation Identifier getName(); @Nullable TypeParameters getTypeParameters(); @Nullable TypeTree getExtends(); @Nullable Container 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 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 getLeadingAnnotations(); List getModifiers(); @Nullable TypeParameters getTypeParameters(); @Nullable TypeTree getReturnTypeExpression(); Identifier getName(); List getParameters(); @Nullable Container 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 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 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 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 getLeadingAnnotations(); List getModifiers(); @Nullable TypeTree getTypeExpression(); List 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 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 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 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