0
# Code Generation
1
2
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.
3
4
## Capabilities
5
6
### CodeBlock
7
8
Template-based code generation using format strings with type-safe placeholders. Handles proper Java syntax, escaping, and formatting automatically.
9
10
```java { .api }
11
/**
12
* Template-based code generation with type-safe placeholders
13
*/
14
public final class CodeBlock {
15
// Static factory methods
16
public static CodeBlock of(String format, Object... args);
17
public static CodeBlock join(Iterable<CodeBlock> codeBlocks, String separator);
18
public static Collector<CodeBlock, ?, CodeBlock> joining(String separator);
19
public static Collector<CodeBlock, ?, CodeBlock> joining(String separator, String prefix, String suffix);
20
public static Builder builder();
21
22
// Instance methods
23
public boolean isEmpty();
24
public Builder toBuilder();
25
public boolean equals(Object o);
26
public int hashCode();
27
public String toString();
28
}
29
```
30
31
**Template Placeholders:**
32
33
- `$L` - **Literals** - Direct substitution without escaping
34
- `$S` - **Strings** - Adds quotes and escapes special characters
35
- `$T` - **Types** - Type references with automatic import management
36
- `$N` - **Names** - References to other generated elements
37
38
**Usage Examples:**
39
40
```java
41
// Literal substitution ($L)
42
CodeBlock literals = CodeBlock.of("int count = $L", 42);
43
// Result: int count = 42
44
45
CodeBlock booleanLiteral = CodeBlock.of("boolean flag = $L", true);
46
// Result: boolean flag = true
47
48
// String substitution ($S)
49
CodeBlock strings = CodeBlock.of("String message = $S", "Hello, World!");
50
// Result: String message = "Hello, World!"
51
52
CodeBlock escapedString = CodeBlock.of("String path = $S", "C:\\Users\\Name");
53
// Result: String path = "C:\\Users\\Name"
54
55
// Type substitution ($T)
56
CodeBlock types = CodeBlock.of("$T list = new $T<>()", List.class, ArrayList.class);
57
// Result: List list = new ArrayList<>();
58
// Automatically adds imports for List and ArrayList
59
60
// Name substitution ($N)
61
MethodSpec helper = MethodSpec.methodBuilder("helperMethod").build();
62
CodeBlock names = CodeBlock.of("$N()", helper);
63
// Result: helperMethod()
64
65
// Combined usage
66
CodeBlock combined = CodeBlock.of(
67
"$T.out.println($S + $N() + $L)",
68
System.class, "Result: ", helper, 42
69
);
70
// Result: System.out.println("Result: " + helperMethod() + 42)
71
```
72
73
### CodeBlock.Builder
74
75
Builder for constructing complex code blocks with multiple statements and control flow.
76
77
```java { .api }
78
/**
79
* Builder for constructing complex code blocks
80
*/
81
public static final class Builder {
82
// Adding code
83
public Builder add(String format, Object... args);
84
public Builder addNamed(String format, Map<String, ?> arguments);
85
public Builder add(CodeBlock codeBlock);
86
87
// Statements and control flow
88
public Builder addStatement(String format, Object... args);
89
public Builder beginControlFlow(String controlFlow, Object... args);
90
public Builder nextControlFlow(String controlFlow, Object... args);
91
public Builder endControlFlow();
92
public Builder endControlFlow(String controlFlow, Object... args);
93
94
// Indentation
95
public Builder indent();
96
public Builder unindent();
97
98
// Building
99
public CodeBlock build();
100
}
101
```
102
103
**Usage Examples:**
104
105
```java
106
// Simple statements
107
CodeBlock statements = CodeBlock.builder()
108
.addStatement("int x = $L", 10)
109
.addStatement("int y = x * $L", 2)
110
.addStatement("$T.out.println($S + y)", System.class, "Result: ")
111
.build();
112
113
// Control flow - if/else
114
CodeBlock ifElse = CodeBlock.builder()
115
.beginControlFlow("if ($N != null)", someVariable)
116
.addStatement("return $N.toString()", someVariable)
117
.nextControlFlow("else")
118
.addStatement("return $S", "null")
119
.endControlFlow()
120
.build();
121
122
// Loops
123
CodeBlock forLoop = CodeBlock.builder()
124
.addStatement("$T<$T> result = new $T<>()", List.class, String.class, ArrayList.class)
125
.beginControlFlow("for (int i = 0; i < $N.size(); i++)", items)
126
.addStatement("$T item = $N.get(i)", String.class, items)
127
.beginControlFlow("if (item != null)")
128
.addStatement("result.add(item.toUpperCase())")
129
.endControlFlow()
130
.endControlFlow()
131
.addStatement("return result")
132
.build();
133
134
// Try-catch blocks
135
CodeBlock tryCatch = CodeBlock.builder()
136
.beginControlFlow("try")
137
.addStatement("return processData(input)")
138
.nextControlFlow("catch ($T e)", IOException.class)
139
.addStatement("$T.error($S, e)", Logger.class, "Failed to process data")
140
.addStatement("throw new $T($S, e)", RuntimeException.class, "Processing failed")
141
.endControlFlow()
142
.build();
143
144
// Switch statements
145
CodeBlock switchBlock = CodeBlock.builder()
146
.beginControlFlow("switch (type)")
147
.add("case $S:\n", "STRING")
148
.indent()
149
.addStatement("return processString(value)")
150
.unindent()
151
.add("case $S:\n", "INTEGER")
152
.indent()
153
.addStatement("return processInteger(value)")
154
.unindent()
155
.add("default:\n")
156
.indent()
157
.addStatement("throw new $T($S + type)", IllegalArgumentException.class, "Unknown type: ")
158
.unindent()
159
.endControlFlow()
160
.build();
161
```
162
163
### Advanced CodeBlock Patterns
164
165
#### Named Arguments
166
167
For complex templates with many parameters:
168
169
```java
170
Map<String, Object> args = new HashMap<>();
171
args.put("className", "UserService");
172
args.put("fieldName", "userRepository");
173
args.put("methodName", "findUser");
174
args.put("paramType", Long.class);
175
176
CodeBlock namedTemplate = CodeBlock.builder()
177
.addNamed("public class $className:T {\n", args)
178
.addNamed(" private final $fieldType:T $fieldName:L;\n",
179
Map.of("fieldType", UserRepository.class, "fieldName", "userRepository"))
180
.addNamed(" \n")
181
.addNamed(" public $returnType:T $methodName:L($paramType:T id) {\n",
182
Map.of("returnType", User.class, "methodName", "findUser", "paramType", Long.class))
183
.addNamed(" return $fieldName:L.findById(id);\n", args)
184
.addNamed(" }\n")
185
.addNamed("}\n")
186
.build();
187
```
188
189
#### Positional Arguments
190
191
For reusing arguments in different positions:
192
193
```java
194
CodeBlock positional = CodeBlock.builder()
195
.add("$2T $1L = new $2T($3S)", "message", String.class, "Hello")
196
.build();
197
// Result: String message = new String("Hello")
198
```
199
200
#### Joining Code Blocks
201
202
```java
203
List<CodeBlock> statements = Arrays.asList(
204
CodeBlock.of("first()"),
205
CodeBlock.of("second()"),
206
CodeBlock.of("third()")
207
);
208
209
// Join with comma separator
210
CodeBlock joined = CodeBlock.join(statements, ", ");
211
// Result: first(), second(), third()
212
213
// Using stream collectors
214
CodeBlock streamJoined = statements.stream()
215
.collect(CodeBlock.joining(", ", "Arrays.asList(", ")"));
216
// Result: Arrays.asList(first(), second(), third())
217
```
218
219
### NameAllocator
220
221
Utility for generating unique, valid Java identifiers and managing name conflicts.
222
223
```java { .api }
224
/**
225
* Utility for allocating unique Java identifier names
226
*/
227
public final class NameAllocator implements Cloneable {
228
// Constructor
229
public NameAllocator();
230
231
// Name allocation
232
public String newName(String suggestion);
233
public String newName(String suggestion, Object tag);
234
public String get(Object tag);
235
236
// Static utility
237
public static String toJavaIdentifier(String suggestion);
238
239
// Cloning
240
public NameAllocator clone();
241
}
242
```
243
244
**Usage Examples:**
245
246
```java
247
// Basic name allocation
248
NameAllocator nameAllocator = new NameAllocator();
249
250
String name1 = nameAllocator.newName("count"); // "count"
251
String name2 = nameAllocator.newName("count"); // "count_"
252
String name3 = nameAllocator.newName("count"); // "count__"
253
254
// Tagged name allocation
255
nameAllocator.newName("value", "field");
256
nameAllocator.newName("value", "parameter"); // Different tag, same name allowed
257
258
String fieldName = nameAllocator.get("field"); // "value"
259
String paramName = nameAllocator.get("parameter"); // "value_"
260
261
// Java identifier conversion
262
String validName1 = NameAllocator.toJavaIdentifier("class"); // "class_"
263
String validName2 = NameAllocator.toJavaIdentifier("2invalid"); // "_2invalid"
264
String validName3 = NameAllocator.toJavaIdentifier("with-dash"); // "with_dash"
265
String validName4 = NameAllocator.toJavaIdentifier("with space"); // "with_space"
266
267
// Practical usage in code generation
268
NameAllocator allocator = new NameAllocator();
269
270
// Reserve Java keywords
271
allocator.newName("class", "reserved");
272
allocator.newName("interface", "reserved");
273
274
// Generate method parameters
275
String userParam = allocator.newName("user", "param");
276
String contextParam = allocator.newName("context", "param");
277
278
// Generate local variables
279
String resultVar = allocator.newName("result", "local");
280
String tempVar = allocator.newName("temp", "local");
281
282
MethodSpec method = MethodSpec.methodBuilder("processUser")
283
.addParameter(User.class, userParam)
284
.addParameter(Context.class, contextParam)
285
.addStatement("$T $N = new $T()", ProcessResult.class, resultVar, ProcessResult.class)
286
.addStatement("$T $N", Object.class, tempVar)
287
.addStatement("// Use allocated names: $N, $N, $N, $N",
288
userParam, contextParam, resultVar, tempVar)
289
.build();
290
```
291
292
### Code Generation Best Practices
293
294
#### Safe String Building
295
296
```java
297
// Avoid direct string concatenation
298
// BAD: "return " + expression + ";"
299
// GOOD: Use CodeBlock templates
300
CodeBlock safeReturn = CodeBlock.of("return $L", expression);
301
302
// Proper escaping for strings
303
CodeBlock properString = CodeBlock.of("String message = $S", userInput);
304
// Automatically handles quotes and escaping
305
```
306
307
#### Type-Safe Templates
308
309
```java
310
// Type references ensure proper imports
311
CodeBlock typeSafe = CodeBlock.of(
312
"$T<$T> list = $T.asList($L, $L, $L)",
313
List.class, String.class, Arrays.class,
314
"first", "second", "third"
315
);
316
317
// Generates proper imports and code:
318
// import java.util.List;
319
// import java.util.Arrays;
320
// List<String> list = Arrays.asList("first", "second", "third");
321
```
322
323
#### Complex Code Generation
324
325
```java
326
// Building a complete method with complex logic
327
public MethodSpec generateProcessorMethod(List<String> operations) {
328
NameAllocator names = new NameAllocator();
329
String inputParam = names.newName("input", "param");
330
String resultVar = names.newName("result", "local");
331
332
CodeBlock.Builder body = CodeBlock.builder()
333
.addStatement("$T $N = new $T()", ProcessResult.class, resultVar, ProcessResult.class);
334
335
for (String operation : operations) {
336
String operationVar = names.newName(operation.toLowerCase(), "operation");
337
body.addStatement("$T $N = perform$L($N)",
338
Object.class, operationVar,
339
operation.substring(0, 1).toUpperCase() + operation.substring(1),
340
inputParam);
341
body.addStatement("$N.add($N)", resultVar, operationVar);
342
}
343
344
body.addStatement("return $N", resultVar);
345
346
return MethodSpec.methodBuilder("process")
347
.addModifiers(Modifier.PUBLIC)
348
.addParameter(Object.class, inputParam)
349
.returns(ProcessResult.class)
350
.addCode(body.build())
351
.build();
352
}
353
```
354
355
#### Error Handling in Code Generation
356
357
```java
358
// Generate proper exception handling
359
CodeBlock errorHandling = CodeBlock.builder()
360
.beginControlFlow("try")
361
.addStatement("return riskyOperation()")
362
.nextControlFlow("catch ($T e)", IOException.class)
363
.addStatement("$T.error($S, e)", Logger.class, "Operation failed")
364
.addStatement("throw new $T(e.getMessage(), e)", ProcessingException.class)
365
.nextControlFlow("finally")
366
.addStatement("cleanup()")
367
.endControlFlow()
368
.build();
369
```