CtrlK
BlogDocsLog inGet started
Tessl Logo

tessl/maven-com-squareup--javapoet

Java API for generating .java source files programmatically with fluent builder interfaces.

Pending
Overview
Eval results
Files

code-generation.mddocs/

Code Generation

Template-based code generation system and utilities for generating method bodies, initializers, and managing identifier names. This system provides safe string-based code generation with proper escaping and formatting.

Capabilities

CodeBlock

Template-based code generation using format strings with type-safe placeholders. Handles proper Java syntax, escaping, and formatting automatically.

/**
 * Template-based code generation with type-safe placeholders
 */
public final class CodeBlock {
    // Static factory methods
    public static CodeBlock of(String format, Object... args);
    public static CodeBlock join(Iterable<CodeBlock> codeBlocks, String separator);
    public static Collector<CodeBlock, ?, CodeBlock> joining(String separator);
    public static Collector<CodeBlock, ?, CodeBlock> joining(String separator, String prefix, String suffix);
    public static Builder builder();
    
    // Instance methods
    public boolean isEmpty();
    public Builder toBuilder();
    public boolean equals(Object o);
    public int hashCode();
    public String toString();
}

Template Placeholders:

  • $L - Literals - Direct substitution without escaping
  • $S - Strings - Adds quotes and escapes special characters
  • $T - Types - Type references with automatic import management
  • $N - Names - References to other generated elements

Usage Examples:

// Literal substitution ($L)
CodeBlock literals = CodeBlock.of("int count = $L", 42);
// Result: int count = 42

CodeBlock booleanLiteral = CodeBlock.of("boolean flag = $L", true);
// Result: boolean flag = true

// String substitution ($S) 
CodeBlock strings = CodeBlock.of("String message = $S", "Hello, World!");
// Result: String message = "Hello, World!"

CodeBlock escapedString = CodeBlock.of("String path = $S", "C:\\Users\\Name");
// Result: String path = "C:\\Users\\Name"

// Type substitution ($T)
CodeBlock types = CodeBlock.of("$T list = new $T<>()", List.class, ArrayList.class);
// Result: List list = new ArrayList<>();
// Automatically adds imports for List and ArrayList

// Name substitution ($N)
MethodSpec helper = MethodSpec.methodBuilder("helperMethod").build();
CodeBlock names = CodeBlock.of("$N()", helper);
// Result: helperMethod()

// Combined usage
CodeBlock combined = CodeBlock.of(
    "$T.out.println($S + $N() + $L)",
    System.class, "Result: ", helper, 42
);
// Result: System.out.println("Result: " + helperMethod() + 42)

CodeBlock.Builder

Builder for constructing complex code blocks with multiple statements and control flow.

/**
 * Builder for constructing complex code blocks
 */
public static final class Builder {
    // Adding code
    public Builder add(String format, Object... args);
    public Builder addNamed(String format, Map<String, ?> arguments);
    public Builder add(CodeBlock codeBlock);
    
    // Statements and control flow
    public Builder addStatement(String format, Object... args);
    public Builder beginControlFlow(String controlFlow, Object... args);
    public Builder nextControlFlow(String controlFlow, Object... args);
    public Builder endControlFlow();
    public Builder endControlFlow(String controlFlow, Object... args);
    
    // Indentation
    public Builder indent();
    public Builder unindent();
    
    // Building
    public CodeBlock build();
}

Usage Examples:

// Simple statements
CodeBlock statements = CodeBlock.builder()
    .addStatement("int x = $L", 10)
    .addStatement("int y = x * $L", 2)
    .addStatement("$T.out.println($S + y)", System.class, "Result: ")
    .build();

// Control flow - if/else
CodeBlock ifElse = CodeBlock.builder()
    .beginControlFlow("if ($N != null)", someVariable)
    .addStatement("return $N.toString()", someVariable)
    .nextControlFlow("else")
    .addStatement("return $S", "null")
    .endControlFlow()
    .build();

// Loops
CodeBlock forLoop = CodeBlock.builder()
    .addStatement("$T<$T> result = new $T<>()", List.class, String.class, ArrayList.class)
    .beginControlFlow("for (int i = 0; i < $N.size(); i++)", items)
    .addStatement("$T item = $N.get(i)", String.class, items)
    .beginControlFlow("if (item != null)")
    .addStatement("result.add(item.toUpperCase())")
    .endControlFlow()
    .endControlFlow()
    .addStatement("return result")
    .build();

// Try-catch blocks
CodeBlock tryCatch = CodeBlock.builder()
    .beginControlFlow("try")
    .addStatement("return processData(input)")
    .nextControlFlow("catch ($T e)", IOException.class)
    .addStatement("$T.error($S, e)", Logger.class, "Failed to process data")
    .addStatement("throw new $T($S, e)", RuntimeException.class, "Processing failed")
    .endControlFlow()
    .build();

// Switch statements
CodeBlock switchBlock = CodeBlock.builder()
    .beginControlFlow("switch (type)")
    .add("case $S:\n", "STRING")
    .indent()
    .addStatement("return processString(value)")
    .unindent()
    .add("case $S:\n", "INTEGER")
    .indent()
    .addStatement("return processInteger(value)")
    .unindent()
    .add("default:\n")
    .indent()
    .addStatement("throw new $T($S + type)", IllegalArgumentException.class, "Unknown type: ")
    .unindent()
    .endControlFlow()
    .build();

Advanced CodeBlock Patterns

Named Arguments

For complex templates with many parameters:

Map<String, Object> args = new HashMap<>();
args.put("className", "UserService");
args.put("fieldName", "userRepository");
args.put("methodName", "findUser");
args.put("paramType", Long.class);

CodeBlock namedTemplate = CodeBlock.builder()
    .addNamed("public class $className:T {\n", args)
    .addNamed("  private final $fieldType:T $fieldName:L;\n", 
        Map.of("fieldType", UserRepository.class, "fieldName", "userRepository"))
    .addNamed("  \n")
    .addNamed("  public $returnType:T $methodName:L($paramType:T id) {\n", 
        Map.of("returnType", User.class, "methodName", "findUser", "paramType", Long.class))
    .addNamed("    return $fieldName:L.findById(id);\n", args)
    .addNamed("  }\n")
    .addNamed("}\n")
    .build();

Positional Arguments

For reusing arguments in different positions:

CodeBlock positional = CodeBlock.builder()
    .add("$2T $1L = new $2T($3S)", "message", String.class, "Hello")
    .build();
// Result: String message = new String("Hello")

Joining Code Blocks

List<CodeBlock> statements = Arrays.asList(
    CodeBlock.of("first()"),
    CodeBlock.of("second()"),
    CodeBlock.of("third()")
);

// Join with comma separator
CodeBlock joined = CodeBlock.join(statements, ", ");
// Result: first(), second(), third()

// Using stream collectors
CodeBlock streamJoined = statements.stream()
    .collect(CodeBlock.joining(", ", "Arrays.asList(", ")"));
// Result: Arrays.asList(first(), second(), third())

NameAllocator

Utility for generating unique, valid Java identifiers and managing name conflicts.

/**
 * Utility for allocating unique Java identifier names
 */
public final class NameAllocator implements Cloneable {
    // Constructor
    public NameAllocator();
    
    // Name allocation
    public String newName(String suggestion);
    public String newName(String suggestion, Object tag);
    public String get(Object tag);
    
    // Static utility
    public static String toJavaIdentifier(String suggestion);
    
    // Cloning
    public NameAllocator clone();
}

Usage Examples:

// Basic name allocation
NameAllocator nameAllocator = new NameAllocator();

String name1 = nameAllocator.newName("count");     // "count"
String name2 = nameAllocator.newName("count");     // "count_"
String name3 = nameAllocator.newName("count");     // "count__"

// Tagged name allocation
nameAllocator.newName("value", "field");
nameAllocator.newName("value", "parameter");  // Different tag, same name allowed

String fieldName = nameAllocator.get("field");      // "value"
String paramName = nameAllocator.get("parameter");  // "value_"

// Java identifier conversion
String validName1 = NameAllocator.toJavaIdentifier("class");      // "class_"
String validName2 = NameAllocator.toJavaIdentifier("2invalid");   // "_2invalid"
String validName3 = NameAllocator.toJavaIdentifier("with-dash");  // "with_dash"
String validName4 = NameAllocator.toJavaIdentifier("with space"); // "with_space"

// Practical usage in code generation
NameAllocator allocator = new NameAllocator();

// Reserve Java keywords
allocator.newName("class", "reserved");
allocator.newName("interface", "reserved");

// Generate method parameters
String userParam = allocator.newName("user", "param");
String contextParam = allocator.newName("context", "param");

// Generate local variables
String resultVar = allocator.newName("result", "local");
String tempVar = allocator.newName("temp", "local");

MethodSpec method = MethodSpec.methodBuilder("processUser")
    .addParameter(User.class, userParam)
    .addParameter(Context.class, contextParam)
    .addStatement("$T $N = new $T()", ProcessResult.class, resultVar, ProcessResult.class)
    .addStatement("$T $N", Object.class, tempVar)
    .addStatement("// Use allocated names: $N, $N, $N, $N", 
        userParam, contextParam, resultVar, tempVar)
    .build();

Code Generation Best Practices

Safe String Building

// Avoid direct string concatenation
// BAD: "return " + expression + ";"
// GOOD: Use CodeBlock templates
CodeBlock safeReturn = CodeBlock.of("return $L", expression);

// Proper escaping for strings
CodeBlock properString = CodeBlock.of("String message = $S", userInput);
// Automatically handles quotes and escaping

Type-Safe Templates

// Type references ensure proper imports
CodeBlock typeSafe = CodeBlock.of(
    "$T<$T> list = $T.asList($L, $L, $L)",
    List.class, String.class, Arrays.class,
    "first", "second", "third"
);

// Generates proper imports and code:
// import java.util.List;
// import java.util.Arrays;
// List<String> list = Arrays.asList("first", "second", "third");

Complex Code Generation

// Building a complete method with complex logic
public MethodSpec generateProcessorMethod(List<String> operations) {
    NameAllocator names = new NameAllocator();
    String inputParam = names.newName("input", "param");
    String resultVar = names.newName("result", "local");
    
    CodeBlock.Builder body = CodeBlock.builder()
        .addStatement("$T $N = new $T()", ProcessResult.class, resultVar, ProcessResult.class);
    
    for (String operation : operations) {
        String operationVar = names.newName(operation.toLowerCase(), "operation");
        body.addStatement("$T $N = perform$L($N)", 
            Object.class, operationVar, 
            operation.substring(0, 1).toUpperCase() + operation.substring(1),
            inputParam);
        body.addStatement("$N.add($N)", resultVar, operationVar);
    }
    
    body.addStatement("return $N", resultVar);
    
    return MethodSpec.methodBuilder("process")
        .addModifiers(Modifier.PUBLIC)
        .addParameter(Object.class, inputParam)
        .returns(ProcessResult.class)
        .addCode(body.build())
        .build();
}

Error Handling in Code Generation

// Generate proper exception handling
CodeBlock errorHandling = CodeBlock.builder()
    .beginControlFlow("try")
    .addStatement("return riskyOperation()")
    .nextControlFlow("catch ($T e)", IOException.class)
    .addStatement("$T.error($S, e)", Logger.class, "Operation failed")
    .addStatement("throw new $T(e.getMessage(), e)", ProcessingException.class)
    .nextControlFlow("finally")
    .addStatement("cleanup()")
    .endControlFlow()
    .build();

Install with Tessl CLI

npx tessl i tessl/maven-com-squareup--javapoet

docs

advanced-types.md

code-generation.md

code-specifications.md

file-management.md

index.md

type-system.md

tile.json