• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 // Copyright 2020 Google LLC
2 //
3 // Licensed under the Apache License, Version 2.0 (the "License");
4 // you may not use this file except in compliance with the License.
5 // You may obtain a copy of the License at
6 //
7 //      http://www.apache.org/licenses/LICENSE-2.0
8 //
9 // Unless required by applicable law or agreed to in writing, software
10 // distributed under the License is distributed on an "AS IS" BASIS,
11 // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12 // See the License for the specific language governing permissions and
13 // limitations under the License.
14 
15 package com.google.api.generator.engine.writer;
16 
17 import static com.google.common.truth.Truth.assertThat;
18 import static org.junit.Assert.assertEquals;
19 import static org.junit.Assert.assertThrows;
20 
21 import com.google.api.generator.engine.ast.AnnotationNode;
22 import com.google.api.generator.engine.ast.AnonymousClassExpr;
23 import com.google.api.generator.engine.ast.ArithmeticOperationExpr;
24 import com.google.api.generator.engine.ast.ArrayExpr;
25 import com.google.api.generator.engine.ast.AssignmentExpr;
26 import com.google.api.generator.engine.ast.AssignmentOperationExpr;
27 import com.google.api.generator.engine.ast.BlockComment;
28 import com.google.api.generator.engine.ast.BlockStatement;
29 import com.google.api.generator.engine.ast.BreakStatement;
30 import com.google.api.generator.engine.ast.CastExpr;
31 import com.google.api.generator.engine.ast.ClassDefinition;
32 import com.google.api.generator.engine.ast.CommentStatement;
33 import com.google.api.generator.engine.ast.ConcreteReference;
34 import com.google.api.generator.engine.ast.EmptyLineStatement;
35 import com.google.api.generator.engine.ast.EnumRefExpr;
36 import com.google.api.generator.engine.ast.Expr;
37 import com.google.api.generator.engine.ast.ExprStatement;
38 import com.google.api.generator.engine.ast.ForStatement;
39 import com.google.api.generator.engine.ast.GeneralForStatement;
40 import com.google.api.generator.engine.ast.IdentifierNode;
41 import com.google.api.generator.engine.ast.IfStatement;
42 import com.google.api.generator.engine.ast.InstanceofExpr;
43 import com.google.api.generator.engine.ast.JavaDocComment;
44 import com.google.api.generator.engine.ast.LambdaExpr;
45 import com.google.api.generator.engine.ast.LineComment;
46 import com.google.api.generator.engine.ast.LogicalOperationExpr;
47 import com.google.api.generator.engine.ast.MethodDefinition;
48 import com.google.api.generator.engine.ast.MethodInvocationExpr;
49 import com.google.api.generator.engine.ast.NewObjectExpr;
50 import com.google.api.generator.engine.ast.NullObjectValue;
51 import com.google.api.generator.engine.ast.PackageInfoDefinition;
52 import com.google.api.generator.engine.ast.PrimitiveValue;
53 import com.google.api.generator.engine.ast.Reference;
54 import com.google.api.generator.engine.ast.ReferenceConstructorExpr;
55 import com.google.api.generator.engine.ast.RelationalOperationExpr;
56 import com.google.api.generator.engine.ast.ReturnExpr;
57 import com.google.api.generator.engine.ast.ScopeNode;
58 import com.google.api.generator.engine.ast.Statement;
59 import com.google.api.generator.engine.ast.StringObjectValue;
60 import com.google.api.generator.engine.ast.SuperObjectValue;
61 import com.google.api.generator.engine.ast.SynchronizedStatement;
62 import com.google.api.generator.engine.ast.TernaryExpr;
63 import com.google.api.generator.engine.ast.ThisObjectValue;
64 import com.google.api.generator.engine.ast.ThrowExpr;
65 import com.google.api.generator.engine.ast.TryCatchStatement;
66 import com.google.api.generator.engine.ast.TypeNode;
67 import com.google.api.generator.engine.ast.UnaryOperationExpr;
68 import com.google.api.generator.engine.ast.Value;
69 import com.google.api.generator.engine.ast.ValueExpr;
70 import com.google.api.generator.engine.ast.VaporReference;
71 import com.google.api.generator.engine.ast.Variable;
72 import com.google.api.generator.engine.ast.VariableExpr;
73 import com.google.api.generator.engine.ast.WhileStatement;
74 import com.google.api.generator.testutils.LineFormatter;
75 import com.google.api.generator.util.TestUtils;
76 import com.google.common.base.Function;
77 import java.io.IOException;
78 import java.util.Arrays;
79 import java.util.Collections;
80 import java.util.HashMap;
81 import java.util.List;
82 import java.util.Map;
83 import java.util.concurrent.TimeoutException;
84 import javax.annotation.Generated;
85 import org.junit.Before;
86 import org.junit.Test;
87 
88 public class JavaWriterVisitorTest {
89   private JavaWriterVisitor writerVisitor;
90 
91   @Before
setUp()92   public void setUp() {
93     writerVisitor = new JavaWriterVisitor();
94   }
95 
96   @Test
writeIdentifier()97   public void writeIdentifier() {
98     String idName = "foobar";
99     IdentifierNode.builder().setName(idName).build().accept(writerVisitor);
100     assertEquals(idName, writerVisitor.write());
101   }
102 
103   @Test
writePrimitiveType()104   public void writePrimitiveType() {
105     TypeNode intType = TypeNode.INT;
106     assertThat(intType).isNotNull();
107     intType.accept(writerVisitor);
108     assertEquals("int", writerVisitor.write());
109   }
110 
111   @Test
writePrimitiveArrayType()112   public void writePrimitiveArrayType() {
113     TypeNode byteArrayType =
114         TypeNode.builder().setTypeKind(TypeNode.TypeKind.BYTE).setIsArray(true).build();
115     assertThat(byteArrayType).isNotNull();
116     byteArrayType.accept(writerVisitor);
117     assertEquals("byte[]", writerVisitor.write());
118   }
119 
120   @Test
writeReferenceType_basic()121   public void writeReferenceType_basic() {
122     TypeNode.withReference(ConcreteReference.withClazz(List.class)).accept(writerVisitor);
123     assertEquals("List", writerVisitor.write());
124 
125     writerVisitor.clear();
126     TypeNode.withReference(
127             VaporReference.builder().setName("FooBar").setPakkage("com.foo.bar").build())
128         .accept(writerVisitor);
129     assertEquals("FooBar", writerVisitor.write());
130   }
131 
132   @Test
writeReferenceType_useFullName()133   public void writeReferenceType_useFullName() {
134     TypeNode.withReference(
135             ConcreteReference.builder().setClazz(List.class).setUseFullName(true).build())
136         .accept(writerVisitor);
137     assertEquals("java.util.List", writerVisitor.write());
138 
139     writerVisitor.clear();
140     TypeNode.withReference(
141             VaporReference.builder()
142                 .setName("FooBar")
143                 .setPakkage("com.foo.bar")
144                 .setUseFullName(true)
145                 .build())
146         .accept(writerVisitor);
147     assertEquals("com.foo.bar.FooBar", writerVisitor.write());
148   }
149 
150   @Test
writeAnnotation_simple()151   public void writeAnnotation_simple() {
152     AnnotationNode annotation = AnnotationNode.OVERRIDE;
153     annotation.accept(writerVisitor);
154     assertEquals("@Override\n", writerVisitor.write());
155   }
156 
157   @Test
writeAnnotation_withStringDescription()158   public void writeAnnotation_withStringDescription() {
159     AnnotationNode annotation = AnnotationNode.withSuppressWarnings("all");
160     annotation.accept(writerVisitor);
161     assertEquals("@SuppressWarnings(\"all\")\n", writerVisitor.write());
162   }
163 
164   @Test
writeAnnotation_withValueDescription()165   public void writeAnnotation_withValueDescription() {
166     TypeNode fakeAnnotationType =
167         TypeNode.withReference(
168             VaporReference.builder().setName("FakeAnnotation").setPakkage("com.foo.bar").build());
169     AnnotationNode annotation =
170         AnnotationNode.builder()
171             .setType(fakeAnnotationType)
172             .setDescription(
173                 ValueExpr.withValue(
174                     PrimitiveValue.builder().setValue("12").setType(TypeNode.INT).build()))
175             .build();
176     annotation.accept(writerVisitor);
177     assertEquals("@FakeAnnotation(12)\n", writerVisitor.write());
178   }
179 
180   @Test
writeAnnotation_withVariableExprDescription()181   public void writeAnnotation_withVariableExprDescription() {
182     TypeNode conditionalOnPropertyType =
183         TypeNode.withReference(
184             VaporReference.builder()
185                 .setName("ConditionalOnClass")
186                 .setPakkage("org.springframework.boot.autoconfigure.condition")
187                 .build());
188     TypeNode clazzType =
189         TypeNode.withReference(
190             VaporReference.builder()
191                 .setName("VisionServiceClient")
192                 .setPakkage("com.foo.bar")
193                 .build());
194 
195     AnnotationNode annotation =
196         AnnotationNode.builder()
197             .setType(conditionalOnPropertyType)
198             .setDescription(
199                 VariableExpr.builder()
200                     .setVariable(
201                         Variable.builder().setType(TypeNode.CLASS_OBJECT).setName("class").build())
202                     .setStaticReferenceType(clazzType)
203                     .build())
204             .build();
205     annotation.accept(writerVisitor);
206     assertEquals("@ConditionalOnClass(VisionServiceClient.class)\n", writerVisitor.write());
207   }
208 
209   @Test
writeAnnotation_withMultipleNamedDescriptions()210   public void writeAnnotation_withMultipleNamedDescriptions() {
211     TypeNode conditionalOnPropertyType =
212         TypeNode.withReference(
213             VaporReference.builder()
214                 .setName("ConditionalOnProperty")
215                 .setPakkage("org.springframework.boot.autoconfigure.condition")
216                 .build());
217 
218     AssignmentExpr matchIfMissing =
219         AssignmentExpr.builder()
220             .setVariableExpr(
221                 VariableExpr.withVariable(
222                     Variable.builder().setName("matchIfMissing").setType(TypeNode.BOOLEAN).build()))
223             .setValueExpr(
224                 ValueExpr.withValue(
225                     PrimitiveValue.builder().setValue("false").setType(TypeNode.BOOLEAN).build()))
226             .build();
227     AssignmentExpr valueString =
228         AssignmentExpr.builder()
229             .setVariableExpr(
230                 VariableExpr.withVariable(
231                     Variable.builder().setName("value").setType(TypeNode.STRING).build()))
232             .setValueExpr(
233                 ValueExpr.withValue(
234                     StringObjectValue.withValue("spring.cloud.gcp.language.enabled")))
235             .build();
236     AnnotationNode annotation =
237         AnnotationNode.builder()
238             .setType(conditionalOnPropertyType)
239             .addDescription(valueString)
240             .addDescription(matchIfMissing)
241             .build();
242     annotation.accept(writerVisitor);
243     assertEquals(
244         "@ConditionalOnProperty(value = \"spring.cloud.gcp.language.enabled\", matchIfMissing = false)\n",
245         writerVisitor.write());
246   }
247 
248   @Test
writeAnnotation_withInvalidDescriptions()249   public void writeAnnotation_withInvalidDescriptions() {
250     TypeNode fakeAnnotationType =
251         TypeNode.withReference(
252             VaporReference.builder().setName("FakeAnnotation").setPakkage("com.foo.bar").build());
253     String stringA = "a string";
254     String stringB = "another string";
255     ValueExpr valueExpr =
256         ValueExpr.withValue(PrimitiveValue.builder().setValue("12").setType(TypeNode.INT).build());
257 
258     VariableExpr clazzVariableExpr =
259         VariableExpr.builder()
260             .setVariable(Variable.builder().setType(TypeNode.CLASS_OBJECT).setName("class").build())
261             .setExprReferenceExpr(
262                 MethodInvocationExpr.builder()
263                     .setMethodName("foobar")
264                     .setReturnType(TypeNode.INT_OBJECT)
265                     .build())
266             .build();
267     AssignmentExpr valueStringAssignmentExpr =
268         AssignmentExpr.builder()
269             .setVariableExpr(
270                 VariableExpr.withVariable(
271                     Variable.builder().setName("value").setType(TypeNode.STRING).build()))
272             .setValueExpr(
273                 ValueExpr.withValue(
274                     StringObjectValue.withValue("spring.cloud.gcp.language.enabled")))
275             .build();
276 
277     AnnotationNode.Builder annotationBuilder =
278         AnnotationNode.builder().setType(fakeAnnotationType).setDescription(stringA);
279 
280     IllegalStateException exceptionForSetDescription =
281         assertThrows(
282             IllegalStateException.class,
283             () -> {
284               annotationBuilder.setDescription(valueExpr);
285             });
286     assertThat(exceptionForSetDescription)
287         .hasMessageThat()
288         .contains("Single parameter with no name cannot be set multiple times");
289 
290     assertThrows(
291         IllegalStateException.class,
292         () -> {
293           annotationBuilder.setDescription(stringB);
294         });
295 
296     assertThrows(
297         IllegalStateException.class,
298         () -> {
299           annotationBuilder.setDescription(clazzVariableExpr);
300         });
301 
302     IllegalStateException exceptionForAddDescription =
303         assertThrows(
304             IllegalStateException.class,
305             () -> {
306               annotationBuilder.addDescription(valueStringAssignmentExpr);
307             });
308     assertThat(exceptionForAddDescription)
309         .hasMessageThat()
310         .contains("Multiple parameters must have names");
311   }
312 
313   @Test
writeAnnotation_withArrayExpr()314   public void writeAnnotation_withArrayExpr() {
315     TypeNode fakeAnnotationType =
316         TypeNode.withReference(
317             VaporReference.builder().setName("FakeAnnotation").setPakkage("com.foo.bar").build());
318     AnnotationNode annotation =
319         AnnotationNode.builder()
320             .setType(fakeAnnotationType)
321             .setDescription(
322                 ArrayExpr.builder()
323                     .setType(TypeNode.createArrayTypeOf(TypeNode.CLASS_OBJECT))
324                     .addExpr(TestUtils.generateClassValueExpr("Class1"))
325                     .addExpr(TestUtils.generateClassValueExpr("Class2"))
326                     .build())
327             .build();
328     annotation.accept(writerVisitor);
329     assertEquals("@FakeAnnotation({Class1.class, Class2.class})\n", writerVisitor.write());
330   }
331 
332   @Test
writeAnnotation_withArrayExprAssignment()333   public void writeAnnotation_withArrayExprAssignment() {
334     TypeNode fakeAnnotationType =
335         TypeNode.withReference(
336             VaporReference.builder().setName("FakeAnnotation").setPakkage("com.foo.bar").build());
337     ArrayExpr arrayExpr =
338         ArrayExpr.builder()
339             .setType(TypeNode.createArrayTypeOf(TypeNode.CLASS_OBJECT))
340             .addExpr(TestUtils.generateClassValueExpr("Class1"))
341             .addExpr(TestUtils.generateClassValueExpr("Class2"))
342             .build();
343     AssignmentExpr clazz1AssignExpr =
344         AssignmentExpr.builder()
345             .setVariableExpr(
346                 VariableExpr.builder()
347                     .setVariable(
348                         Variable.builder()
349                             .setName("value1")
350                             .setType(TypeNode.createArrayTypeOf(TypeNode.CLASS_OBJECT))
351                             .build())
352                     .build())
353             .setValueExpr(arrayExpr)
354             .build();
355     AssignmentExpr clazz2AssignExpr =
356         AssignmentExpr.builder()
357             .setVariableExpr(
358                 VariableExpr.withVariable(
359                     Variable.builder()
360                         .setName("value2")
361                         .setType(TypeNode.createArrayTypeOf(TypeNode.CLASS_OBJECT))
362                         .build()))
363             .setValueExpr(arrayExpr)
364             .build();
365     AnnotationNode annotation =
366         AnnotationNode.builder()
367             .setType(fakeAnnotationType)
368             .addDescription(clazz1AssignExpr)
369             .addDescription(clazz2AssignExpr)
370             .build();
371     annotation.accept(writerVisitor);
372     assertEquals(
373         "@FakeAnnotation(value1 = {Class1.class, Class2.class}, "
374             + "value2 = {Class1.class, Class2.class})\n",
375         writerVisitor.write());
376   }
377 
378   @Test
writeArrayExpr_add1StringExpr()379   public void writeArrayExpr_add1StringExpr() {
380     ArrayExpr expr =
381         ArrayExpr.builder()
382             .setType(TypeNode.createArrayTypeOf(TypeNode.STRING))
383             .addExpr(ValueExpr.builder().setValue(StringObjectValue.withValue("test1")).build())
384             .build();
385     expr.accept(writerVisitor);
386     assertEquals("{\"test1\"}", writerVisitor.write());
387   }
388 
389   @Test
writeArrayExpr_addManyStrExpr()390   public void writeArrayExpr_addManyStrExpr() {
391     ArrayExpr expr =
392         ArrayExpr.builder()
393             .setType(TypeNode.createArrayTypeOf(TypeNode.STRING))
394             .addExpr(TestUtils.generateStringValueExpr("test1"))
395             .addExpr(TestUtils.generateStringValueExpr("test2"))
396             .addExpr(TestUtils.generateStringValueExpr("test3"))
397             .build();
398     expr.accept(writerVisitor);
399     assertEquals("{\"test1\", \"test2\", \"test3\"}", writerVisitor.write());
400   }
401 
402   @Test
writeArrayExpr_addManyClassExpr()403   public void writeArrayExpr_addManyClassExpr() {
404     ArrayExpr expr =
405         ArrayExpr.builder()
406             .setType(TypeNode.createArrayTypeOf(TypeNode.CLASS_OBJECT))
407             .addExpr(TestUtils.generateClassValueExpr("Class1"))
408             .addExpr(TestUtils.generateClassValueExpr("Class2"))
409             .addExpr(TestUtils.generateClassValueExpr("Class3"))
410             .build();
411     expr.accept(writerVisitor);
412     assertEquals("{Class1.class, Class2.class, Class3.class}", writerVisitor.write());
413   }
414 
415   @Test
writeArrayExpr_mixedVariablesStaticAndNormalReference()416   public void writeArrayExpr_mixedVariablesStaticAndNormalReference() {
417     VariableExpr clazzVar =
418         VariableExpr.builder()
419             .setVariable(
420                 Variable.builder().setName("clazz1Var").setType(TypeNode.CLASS_OBJECT).build())
421             .build();
422     ArrayExpr expr =
423         ArrayExpr.builder()
424             .setType(TypeNode.createArrayTypeOf(TypeNode.CLASS_OBJECT))
425             .addExpr(clazzVar)
426             .addExpr(TestUtils.generateClassValueExpr("Class2"))
427             .build();
428     expr.accept(writerVisitor);
429     assertEquals("{clazz1Var, Class2.class}", writerVisitor.write());
430   }
431 
432   @Test
writeArrayExpr_assignemntWithDeclaration()433   public void writeArrayExpr_assignemntWithDeclaration() {
434     VariableExpr varExpr =
435         VariableExpr.builder()
436             .setVariable(
437                 Variable.builder()
438                     .setName("varExpr")
439                     .setType(TypeNode.createArrayTypeOf(TypeNode.STRING))
440                     .build())
441             .setIsDecl(true)
442             .build();
443     ArrayExpr expr =
444         ArrayExpr.builder()
445             .setType(TypeNode.createArrayTypeOf(TypeNode.STRING))
446             .addExpr(TestUtils.generateStringValueExpr("str1"))
447             .addExpr(TestUtils.generateStringValueExpr("str2"))
448             .build();
449     AssignmentExpr assignmentExpr =
450         AssignmentExpr.builder().setVariableExpr(varExpr).setValueExpr(expr).build();
451     assignmentExpr.accept(writerVisitor);
452     assertEquals("String[] varExpr = {\"str1\", \"str2\"}", writerVisitor.write());
453   }
454 
455   @Test
writeNewObjectExpr_basic()456   public void writeNewObjectExpr_basic() {
457     // isGeneric() is true, but generics() is empty.
458     ConcreteReference ref = ConcreteReference.withClazz(List.class);
459     TypeNode type = TypeNode.withReference(ref);
460     NewObjectExpr newObjectExpr = NewObjectExpr.builder().setIsGeneric(true).setType(type).build();
461     newObjectExpr.accept(writerVisitor);
462     assertEquals("new List<>()", writerVisitor.write());
463   }
464 
465   @Test
writeNewObjectExpr_withMethodExprArgs()466   public void writeNewObjectExpr_withMethodExprArgs() {
467     // isGeneric() is false, and generics() is empty.
468     // [Constructing] `new IOException(message, cause())` and `cause()` is a method invocation.
469     TypeNode type = TypeNode.withReference(ConcreteReference.withClazz(IOException.class));
470     Variable message = Variable.builder().setName("message").setType(TypeNode.STRING).build();
471     VariableExpr msgExpr = VariableExpr.builder().setVariable(message).build();
472     MethodInvocationExpr causeExpr =
473         MethodInvocationExpr.builder()
474             .setMethodName("cause")
475             .setReturnType(TypeNode.withReference(ConcreteReference.withClazz(Throwable.class)))
476             .build();
477     NewObjectExpr newObjectExpr =
478         NewObjectExpr.builder()
479             .setType(type)
480             .setArguments(Arrays.asList(msgExpr, causeExpr))
481             .build();
482     newObjectExpr.accept(writerVisitor);
483     assertEquals("new IOException(message, cause())", writerVisitor.write());
484   }
485 
486   @Test
writeNewObjectExpr_withGenericsAndArgs()487   public void writeNewObjectExpr_withGenericsAndArgs() {
488     // isGeneric() is true and generics() is not empty.
489     ConcreteReference listRef =
490         ConcreteReference.builder()
491             .setClazz(List.class)
492             .setGenerics(Arrays.asList(ConcreteReference.withClazz(Integer.class)))
493             .build();
494     ConcreteReference mapRef =
495         ConcreteReference.builder()
496             .setClazz(HashMap.class)
497             .setGenerics(Arrays.asList(ConcreteReference.withClazz(String.class), listRef))
498             .build();
499     TypeNode type = TypeNode.withReference(mapRef);
500 
501     TypeNode someType =
502         TypeNode.withReference(
503             VaporReference.builder()
504                 .setName("SomeClass")
505                 .setPakkage("com.google.api.generator.engine")
506                 .build());
507     MethodInvocationExpr methodExpr =
508         MethodInvocationExpr.builder()
509             .setMethodName("foobar")
510             .setReturnType(TypeNode.INT)
511             .setStaticReferenceType(someType)
512             .build();
513     Variable num = Variable.builder().setName("num").setType(TypeNode.FLOAT).build();
514     VariableExpr numExpr = VariableExpr.builder().setVariable(num).build();
515     NewObjectExpr newObjectExpr =
516         NewObjectExpr.builder()
517             .setIsGeneric(true)
518             .setType(type)
519             .setArguments(Arrays.asList(methodExpr, numExpr))
520             .build();
521     newObjectExpr.accept(writerVisitor);
522     assertEquals(
523         "new HashMap<String, List<Integer>>(SomeClass.foobar(), num)", writerVisitor.write());
524   }
525 
526   /** =============================== EXPRESSIONS =============================== */
527   @Test
writeValueExpr()528   public void writeValueExpr() {
529     Value value = PrimitiveValue.builder().setType(TypeNode.INT).setValue("3").build();
530     ValueExpr valueExpr = ValueExpr.builder().setValue(value).build();
531     valueExpr.accept(writerVisitor);
532     assertEquals("3", writerVisitor.write());
533   }
534 
535   @Test
writeVariableExpr_basic()536   public void writeVariableExpr_basic() {
537     Variable variable = Variable.builder().setName("x").setType(TypeNode.INT).build();
538     VariableExpr variableExpr = VariableExpr.builder().setVariable(variable).build();
539 
540     variableExpr.accept(writerVisitor);
541     assertEquals("x", writerVisitor.write());
542   }
543 
544   @Test
writeVariableExpr_wildcardType()545   public void writeVariableExpr_wildcardType() {
546     TypeNode wildcardListType =
547         TypeNode.withReference(
548             ConcreteReference.builder()
549                 .setClazz(List.class)
550                 .setGenerics(Arrays.asList(TypeNode.WILDCARD_REFERENCE))
551                 .build());
552 
553     Variable variable = Variable.builder().setName("x").setType(wildcardListType).build();
554     VariableExpr variableExpr =
555         VariableExpr.builder().setIsDecl(true).setVariable(variable).build();
556 
557     variableExpr.accept(writerVisitor);
558     assertEquals("List<?> x", writerVisitor.write());
559   }
560 
561   @Test
writeVariableExpr_wildcardTypeWithUpperBound()562   public void writeVariableExpr_wildcardTypeWithUpperBound() {
563     TypeNode wildcardListType =
564         TypeNode.withReference(
565             ConcreteReference.builder()
566                 .setClazz(List.class)
567                 .setGenerics(
568                     Arrays.asList(
569                         ConcreteReference.wildcardWithUpperBound(
570                             ConcreteReference.withClazz(Expr.class))))
571                 .build());
572 
573     Variable variable = Variable.builder().setName("x").setType(wildcardListType).build();
574     VariableExpr variableExpr =
575         VariableExpr.builder().setIsDecl(true).setVariable(variable).build();
576 
577     variableExpr.accept(writerVisitor);
578     assertEquals("List<? extends Expr> x", writerVisitor.write());
579   }
580 
581   @Test
writeVariableExpr_staticReference()582   public void writeVariableExpr_staticReference() {
583     VariableExpr variableExpr =
584         VariableExpr.builder()
585             .setVariable(
586                 Variable.builder().setType(TypeNode.INT_OBJECT).setName("MAX_VALUE").build())
587             .setStaticReferenceType(TypeNode.INT_OBJECT)
588             .build();
589 
590     variableExpr.accept(writerVisitor);
591     assertEquals("Integer.MAX_VALUE", writerVisitor.write());
592   }
593 
594   @Test
writeVariableExpr_nonDeclIgnoresModifiers()595   public void writeVariableExpr_nonDeclIgnoresModifiers() {
596     Variable variable = Variable.builder().setName("x").setType(TypeNode.BOOLEAN).build();
597     VariableExpr expr =
598         VariableExpr.builder()
599             .setVariable(variable)
600             .setScope(ScopeNode.PUBLIC)
601             .setIsStatic(true)
602             .setIsFinal(true)
603             .build();
604 
605     expr.accept(writerVisitor);
606     assertEquals("x", writerVisitor.write());
607   }
608 
609   @Test
writeVariableExpr_basicLocalDecl()610   public void writeVariableExpr_basicLocalDecl() {
611     Variable variable = Variable.builder().setName("x").setType(TypeNode.INT).build();
612     VariableExpr expr = VariableExpr.builder().setVariable(variable).setIsDecl(true).build();
613 
614     expr.accept(writerVisitor);
615     assertEquals("int x", writerVisitor.write());
616   }
617 
618   @Test
writeVariableExpr_localFinalDecl()619   public void writeVariableExpr_localFinalDecl() {
620     Variable variable = Variable.builder().setName("x").setType(TypeNode.BOOLEAN).build();
621 
622     VariableExpr expr =
623         VariableExpr.builder().setVariable(variable).setIsFinal(true).setIsDecl(true).build();
624 
625     expr.accept(writerVisitor);
626     assertEquals("final boolean x", writerVisitor.write());
627   }
628 
629   @Test
writeVariableExpr_scopedDecl()630   public void writeVariableExpr_scopedDecl() {
631     Variable variable = Variable.builder().setName("x").setType(TypeNode.INT).build();
632     VariableExpr expr =
633         VariableExpr.builder()
634             .setVariable(variable)
635             .setScope(ScopeNode.PRIVATE)
636             .setIsDecl(true)
637             .build();
638 
639     expr.accept(writerVisitor);
640     assertEquals("private int x", writerVisitor.write());
641   }
642 
643   @Test
writeVariableExpr_scopedStaticFinalDecl()644   public void writeVariableExpr_scopedStaticFinalDecl() {
645     Variable variable = Variable.builder().setName("x").setType(TypeNode.BOOLEAN).build();
646     VariableExpr expr =
647         VariableExpr.builder()
648             .setVariable(variable)
649             .setIsDecl(true)
650             .setScope(ScopeNode.PUBLIC)
651             .setIsStatic(true)
652             .setIsFinal(true)
653             .build();
654 
655     expr.accept(writerVisitor);
656     assertEquals("public static final boolean x", writerVisitor.write());
657   }
658 
659   @Test
writeVariableExpr_scopedStaticFinalVolatileDecl()660   public void writeVariableExpr_scopedStaticFinalVolatileDecl() {
661     Variable variable = Variable.builder().setName("x").setType(TypeNode.BOOLEAN).build();
662     VariableExpr expr =
663         VariableExpr.builder()
664             .setVariable(variable)
665             .setIsDecl(true)
666             .setScope(ScopeNode.PRIVATE)
667             .setIsStatic(true)
668             .setIsFinal(true)
669             .setIsVolatile(true)
670             .build();
671 
672     expr.accept(writerVisitor);
673     assertEquals("private static final volatile boolean x", writerVisitor.write());
674   }
675 
676   @Test
writeVariableExpr_basicReference()677   public void writeVariableExpr_basicReference() {
678     Variable variable = Variable.builder().setName("x").setType(TypeNode.STRING_ARRAY).build();
679     VariableExpr variableExpr = VariableExpr.builder().setVariable(variable).build();
680 
681     Variable subVariable = Variable.builder().setName("length").setType(TypeNode.INT).build();
682     variableExpr =
683         VariableExpr.builder().setVariable(subVariable).setExprReferenceExpr(variableExpr).build();
684     variableExpr.accept(writerVisitor);
685     assertEquals("x.length", writerVisitor.write());
686   }
687 
688   @Test
writeVariableExpr_basicReferenceWithModifiersSet()689   public void writeVariableExpr_basicReferenceWithModifiersSet() {
690     Variable variable = Variable.builder().setName("x").setType(TypeNode.STRING_ARRAY).build();
691     VariableExpr variableExpr = VariableExpr.builder().setVariable(variable).build();
692 
693     Variable subVariable = Variable.builder().setName("length").setType(TypeNode.INT).build();
694     variableExpr =
695         VariableExpr.builder()
696             .setVariable(subVariable)
697             .setExprReferenceExpr(variableExpr)
698             .setScope(ScopeNode.PUBLIC)
699             .setIsFinal(true)
700             .setIsStatic(true)
701             .build();
702     variableExpr.accept(writerVisitor);
703     assertEquals("x.length", writerVisitor.write());
704   }
705 
706   @Test
writeVariableExpr_nestedReference()707   public void writeVariableExpr_nestedReference() {
708     Variable variable = Variable.builder().setName("x").setType(TypeNode.STRING_ARRAY).build();
709     VariableExpr variableExpr = VariableExpr.builder().setVariable(variable).build();
710 
711     Variable subVariable =
712         Variable.builder().setName("someStringField").setType(TypeNode.STRING).build();
713     variableExpr =
714         VariableExpr.builder().setVariable(subVariable).setExprReferenceExpr(variableExpr).build();
715     subVariable = Variable.builder().setName("anotherStringField").setType(TypeNode.STRING).build();
716     variableExpr =
717         VariableExpr.builder().setVariable(subVariable).setExprReferenceExpr(variableExpr).build();
718     subVariable = Variable.builder().setName("lengthField").setType(TypeNode.INT).build();
719     variableExpr =
720         VariableExpr.builder().setVariable(subVariable).setExprReferenceExpr(variableExpr).build();
721     variableExpr.accept(writerVisitor);
722     assertEquals("x.someStringField.anotherStringField.lengthField", writerVisitor.write());
723   }
724 
725   @Test
writeArithmeticOperationExpr_concatStringWithMethod()726   public void writeArithmeticOperationExpr_concatStringWithMethod() {
727     ValueExpr lhsExpr = ValueExpr.withValue(StringObjectValue.withValue("someWord"));
728     MethodInvocationExpr methodInvocationExpr =
729         MethodInvocationExpr.builder().setMethodName("getMethod").build();
730     MethodInvocationExpr rhsExpr =
731         MethodInvocationExpr.builder()
732             .setExprReferenceExpr(methodInvocationExpr)
733             .setMethodName("getString")
734             .setReturnType(TypeNode.STRING)
735             .build();
736     ArithmeticOperationExpr arithmeticOperationExpr =
737         ArithmeticOperationExpr.concatWithExprs(lhsExpr, rhsExpr);
738     arithmeticOperationExpr.accept(writerVisitor);
739     assertThat(writerVisitor.write()).isEqualTo("\"someWord\" + getMethod().getString()");
740   }
741 
742   @Test
writeArithmeticOperationExpr_concatStringWithNumber()743   public void writeArithmeticOperationExpr_concatStringWithNumber() {
744     ValueExpr rhsExpr =
745         ValueExpr.withValue(PrimitiveValue.builder().setType(TypeNode.INT).setValue("5").build());
746     ValueExpr lhsExpr = ValueExpr.withValue(StringObjectValue.withValue("someWord"));
747     ArithmeticOperationExpr arithmeticOperationExpr =
748         ArithmeticOperationExpr.concatWithExprs(lhsExpr, rhsExpr);
749     arithmeticOperationExpr.accept(writerVisitor);
750     assertThat(writerVisitor.write()).isEqualTo("\"someWord\" + 5");
751   }
752 
753   /** =============================== COMMENT =============================== */
754   @Test
writeBlockCommentStatement_basic()755   public void writeBlockCommentStatement_basic() {
756     String content = "this is a test comment";
757     BlockComment blockComment = BlockComment.builder().setComment(content).build();
758     CommentStatement commentStatement = CommentStatement.withComment(blockComment);
759     String expected = LineFormatter.lines("/*\n", "* this is a test comment\n", "*/\n");
760     commentStatement.accept(writerVisitor);
761     assertEquals(expected, writerVisitor.write());
762   }
763 
764   @Test
writeLineCommentStatement_basic()765   public void writeLineCommentStatement_basic() {
766     String content = "this is a test comment";
767     LineComment lineComment = LineComment.builder().setComment(content).build();
768     CommentStatement commentStatement = CommentStatement.withComment(lineComment);
769     String expected = "// this is a test comment\n";
770     commentStatement.accept(writerVisitor);
771     assertEquals(expected, writerVisitor.write());
772   }
773 
774   @Test
writeJavaDocCommentStatement_allComponents()775   public void writeJavaDocCommentStatement_allComponents() {
776     String content = "this is a test comment";
777     String deprecatedText = "Use the {@link ArchivedBookName} class instead.";
778     String paramName = "shelfName";
779     String paramDescription = "The name of the shelf where books are published to.";
780     String paragraph1 =
781         "This class provides the ability to make remote calls to the backing service through"
782             + " method calls that map to API methods. Sample code to get started:";
783     String paragraph2 =
784         "The surface of this class includes several types of Java methods for each of the API's"
785             + " methods:";
786     String sampleCode = createSampleCode();
787     List<String> orderedlList =
788         Arrays.asList("A flattened method.", " A request object method.", "A callable method.");
789     String throwsType = "com.google.api.gax.rpc.ApiException";
790     String throwsDescription = "if the remote call fails.";
791     JavaDocComment javaDocComment =
792         JavaDocComment.builder()
793             .addComment(content)
794             .addParagraph(paragraph1)
795             .addSampleCode(sampleCode)
796             .addParagraph(paragraph2)
797             .addOrderedList(orderedlList)
798             .addSampleCode(sampleCode)
799             .addParam(paramName, paramDescription)
800             .setThrows(throwsType, throwsDescription)
801             .setDeprecated(deprecatedText)
802             .build();
803     CommentStatement commentStatement = CommentStatement.withComment(javaDocComment);
804     String expected =
805         LineFormatter.lines(
806             "/**\n",
807             "* this is a test comment\n",
808             "* <p> This class provides the ability to make remote calls to the backing service"
809                 + " through method calls that map to API methods. Sample code to get started:\n",
810             "* <pre>{@code\n",
811             "* try (boolean condition = false) {\n",
812             "* int x = 3;\n",
813             "* }\n",
814             "* }</pre>\n",
815             "* <p> The surface of this class includes several types of Java methods for each of"
816                 + " the API's methods:\n",
817             "* <ol>\n",
818             "* <li> A flattened method.\n",
819             "* <li>  A request object method.\n",
820             "* <li> A callable method.\n",
821             "* </ol>\n",
822             "* <pre>{@code\n",
823             "* try (boolean condition = false) {\n",
824             "* int x = 3;\n",
825             "* }\n",
826             "* }</pre>\n",
827             "* @param shelfName The name of the shelf where books are published to.\n",
828             "* @throws com.google.api.gax.rpc.ApiException if the remote call fails.\n",
829             "* @deprecated Use the {@link ArchivedBookName} class instead.\n",
830             "*/\n");
831     commentStatement.accept(writerVisitor);
832     assertEquals(expected, writerVisitor.write());
833   }
834 
835   @Test
writeBlockComment_shortLines()836   public void writeBlockComment_shortLines() {
837     String content = "Apache License \nThis is a test file header";
838     BlockComment blockComment = BlockComment.builder().setComment(content).build();
839     String expected =
840         LineFormatter.lines("/*\n", "* Apache License\n", "* This is a test file header\n", "*/\n");
841     blockComment.accept(writerVisitor);
842     assertEquals(expected, writerVisitor.write());
843   }
844 
845   @Test
writeBlockComment_newLineInBetween()846   public void writeBlockComment_newLineInBetween() {
847     String content =
848         "Apache License \n"
849             + "Licensed under the Apache License, Version 2.0 (the \"License\");\n\n"
850             + "you may not use this file except in compliance with the License.";
851     BlockComment blockComment = BlockComment.builder().setComment(content).build();
852     String expected =
853         LineFormatter.lines(
854             "/*\n",
855             "* Apache License\n",
856             "* Licensed under the Apache License, Version 2.0 (the \"License\");\n",
857             "*\n",
858             "* you may not use this file except in compliance with the License.\n",
859             "*/\n");
860     blockComment.accept(writerVisitor);
861     assertEquals(expected, writerVisitor.write());
862   }
863 
864   @Test
writeLineComment_longLine()865   public void writeLineComment_longLine() {
866     String content =
867         "this is a long test comment with so many words, hello world, hello again, hello for 3"
868             + " times, blah, blah!";
869     LineComment lineComment = LineComment.builder().setComment(content).build();
870     String expected =
871         LineFormatter.lines(
872             "// this is a long test comment with so many words, hello world, hello again, hello"
873                 + " for 3 times,\n",
874             "// blah, blah!\n");
875     lineComment.accept(writerVisitor);
876     assertEquals(expected, writerVisitor.write());
877   }
878 
879   @Test
writeLineComment_specialChar()880   public void writeLineComment_specialChar() {
881     String content =
882         "usage: gradle run -PmainClass=com.google.example.examples.library.v1.Hopper"
883             + " [--args='[--shelf \"Novel\\\"`\b\t\n\r"
884             + "\"]']";
885     LineComment lineComment = LineComment.withComment(content);
886     String expected =
887         LineFormatter.lines(
888             "// usage: gradle run -PmainClass=com.google.example.examples.library.v1.Hopper"
889                 + " [--args='[--shelf\n",
890             "// \"Novel\\\\\"`\\b\\t\n",
891             "// \\r",
892             "\"]']\n");
893     lineComment.accept(writerVisitor);
894     assertEquals(expected, writerVisitor.write());
895   }
896 
897   @Test
writeJavaDocComment_specialChar()898   public void writeJavaDocComment_specialChar() {
899     // Only comments and sample codes in JavaDocComment need this escaper.
900     // <p> <ol> <li> <ul> are hard-coded in monolith generator, which do not need escaping.
901     JavaDocComment javaDocComment =
902         JavaDocComment.builder()
903             .addComment(
904                 "The API has a collection of [Shelf][google.example.library.v1.Shelf] resources")
905             .addComment("named `bookShelves/*`")
906             .addSampleCode(
907                 "ApiFuture<Shelf> future ="
908                     + " libraryClient.createShelfCallable().futureCall(request);")
909             .addOrderedList(
910                 Arrays.asList(
911                     "A \"flattened\" method.",
912                     "A \"request object\" method.",
913                     "A \"callable\" method."))
914             .addComment("RPC method comment may include special characters: <>&\"`'@.")
915             .build();
916     String expected =
917         LineFormatter.lines(
918             "/**\n",
919             "* The API has a collection of [Shelf][google.example.library.v1.Shelf] resources\n",
920             "* named `bookShelves/&#42;`\n",
921             "* <pre>{@code\n",
922             "* ApiFuture<Shelf> future ="
923                 + " libraryClient.createShelfCallable().futureCall(request);\n",
924             "* }</pre>\n",
925             "* <ol>\n",
926             "* <li> A \"flattened\" method.\n",
927             "* <li> A \"request object\" method.\n",
928             "* <li> A \"callable\" method.\n",
929             "* </ol>\n",
930             "* RPC method comment may include special characters: &lt;&gt;&amp;\"`'{@literal @}.\n",
931             "*/\n");
932     javaDocComment.accept(writerVisitor);
933     assertEquals(expected, writerVisitor.write());
934   }
935 
936   @Test
writeTernaryExpr_basic()937   public void writeTernaryExpr_basic() {
938     Variable conditionVariable =
939         Variable.builder().setName("condition").setType(TypeNode.BOOLEAN).build();
940     VariableExpr conditionExpr = VariableExpr.builder().setVariable(conditionVariable).build();
941 
942     Value value1 = PrimitiveValue.builder().setType(TypeNode.INT).setValue("3").build();
943     Expr thenExpr = ValueExpr.builder().setValue(value1).build();
944     Value value2 = PrimitiveValue.builder().setType(TypeNode.INT).setValue("4").build();
945     Expr elseExpr = ValueExpr.builder().setValue(value2).build();
946 
947     TernaryExpr ternaryExpr =
948         TernaryExpr.builder()
949             .setConditionExpr(conditionExpr)
950             .setThenExpr(thenExpr)
951             .setElseExpr(elseExpr)
952             .build();
953     ternaryExpr.accept(writerVisitor);
954     assertEquals("condition ? 3 : 4", writerVisitor.write());
955   }
956 
957   @Test
writeAssignmentExpr_basicValue()958   public void writeAssignmentExpr_basicValue() {
959     Variable variable = Variable.builder().setName("x").setType(TypeNode.INT).build();
960     VariableExpr variableExpr =
961         VariableExpr.builder().setVariable(variable).setIsDecl(true).build();
962 
963     Value value = PrimitiveValue.builder().setType(TypeNode.INT).setValue("3").build();
964     Expr valueExpr = ValueExpr.builder().setValue(value).build();
965 
966     AssignmentExpr assignExpr =
967         AssignmentExpr.builder().setVariableExpr(variableExpr).setValueExpr(valueExpr).build();
968 
969     assignExpr.accept(writerVisitor);
970     assertEquals("int x = 3", writerVisitor.write());
971   }
972 
973   @Test
writeAssignmentExpr_varToVar()974   public void writeAssignmentExpr_varToVar() {
975     Variable variable = Variable.builder().setName("foobar").setType(TypeNode.INT).build();
976     VariableExpr variableExpr =
977         VariableExpr.builder()
978             .setVariable(variable)
979             .setIsStatic(true)
980             .setIsFinal(true)
981             .setScope(ScopeNode.PRIVATE)
982             .setIsDecl(true)
983             .build();
984 
985     Variable anotherVariable = Variable.builder().setName("y").setType(TypeNode.INT).build();
986     Expr valueExpr = VariableExpr.builder().setVariable(anotherVariable).build();
987 
988     AssignmentExpr assignExpr =
989         AssignmentExpr.builder().setVariableExpr(variableExpr).setValueExpr(valueExpr).build();
990 
991     assignExpr.accept(writerVisitor);
992     assertEquals("private static final int foobar = y", writerVisitor.write());
993   }
994 
995   @Test
writeAssignmentExpr_nullObjectValueReferenceType()996   public void writeAssignmentExpr_nullObjectValueReferenceType() {
997     Variable variable = Variable.builder().setName("x").setType(TypeNode.STRING).build();
998     VariableExpr variableExpr =
999         VariableExpr.builder().setVariable(variable).setIsDecl(true).build();
1000 
1001     Value value = NullObjectValue.create();
1002     Expr valueExpr = ValueExpr.builder().setValue(value).build();
1003 
1004     AssignmentExpr assignExpr =
1005         AssignmentExpr.builder().setVariableExpr(variableExpr).setValueExpr(valueExpr).build();
1006 
1007     assignExpr.accept(writerVisitor);
1008     assertEquals("String x = null", writerVisitor.write());
1009   }
1010 
1011   @Test
writeStringObjectValue_basic()1012   public void writeStringObjectValue_basic() {
1013     Value value = StringObjectValue.withValue("test");
1014     Expr valueExpr = ValueExpr.builder().setValue(value).build();
1015     valueExpr.accept(writerVisitor);
1016     assertThat(writerVisitor.write()).isEqualTo("\"test\"");
1017   }
1018 
1019   @Test
writeAssignmentExpr_stringObjectValue()1020   public void writeAssignmentExpr_stringObjectValue() {
1021     Variable variable = Variable.builder().setName("x").setType(TypeNode.STRING).build();
1022     VariableExpr variableExpr =
1023         VariableExpr.builder().setVariable(variable).setIsDecl(true).build();
1024 
1025     Value value = StringObjectValue.withValue("Hi! World. \n");
1026     Expr valueExpr = ValueExpr.builder().setValue(value).build();
1027     AssignmentExpr assignExpr =
1028         AssignmentExpr.builder().setVariableExpr(variableExpr).setValueExpr(valueExpr).build();
1029 
1030     assignExpr.accept(writerVisitor);
1031     assertThat(writerVisitor.write()).isEqualTo("String x = \"Hi! World. \\n\"");
1032   }
1033 
1034   @Test
writeAssignmentExpr_variableDeclarationWithAnnotation()1035   public void writeAssignmentExpr_variableDeclarationWithAnnotation() {
1036     Variable variable = Variable.builder().setName("x").setType(TypeNode.STRING).build();
1037     VariableExpr variableExpr =
1038         VariableExpr.builder()
1039             .setVariable(variable)
1040             .setIsDecl(true)
1041             .setAnnotations(Arrays.asList(AnnotationNode.DEPRECATED, AnnotationNode.DEPRECATED))
1042             .build();
1043 
1044     Value value = StringObjectValue.withValue("Hi! World. \n");
1045     Expr valueExpr = ValueExpr.builder().setValue(value).build();
1046     AssignmentExpr assignExpr =
1047         AssignmentExpr.builder().setVariableExpr(variableExpr).setValueExpr(valueExpr).build();
1048 
1049     assignExpr.accept(writerVisitor);
1050     assertThat(writerVisitor.write()).isEqualTo("@Deprecated\nString x = \"Hi! World. \\n\"");
1051   }
1052 
1053   @Test
writeMethodInvocationExpr_basic()1054   public void writeMethodInvocationExpr_basic() {
1055     MethodInvocationExpr methodExpr =
1056         MethodInvocationExpr.builder().setMethodName("foobar").build();
1057 
1058     methodExpr.accept(writerVisitor);
1059     assertEquals("foobar()", writerVisitor.write());
1060   }
1061 
1062   @Test
writeMethodInvocationExpr_staticRef()1063   public void writeMethodInvocationExpr_staticRef() {
1064     TypeNode someType =
1065         TypeNode.withReference(
1066             VaporReference.builder()
1067                 .setName("SomeClass")
1068                 .setPakkage("com.google.api.some.pakkage")
1069                 .build());
1070 
1071     MethodInvocationExpr methodExpr =
1072         MethodInvocationExpr.builder()
1073             .setMethodName("foobar")
1074             .setStaticReferenceType(someType)
1075             .build();
1076 
1077     methodExpr.accept(writerVisitor);
1078     assertEquals("SomeClass.foobar()", writerVisitor.write());
1079   }
1080 
1081   @Test
writeMethodInvocationExpr_genericWithArgs()1082   public void writeMethodInvocationExpr_genericWithArgs() {
1083     Reference mapReference =
1084         ConcreteReference.builder()
1085             .setClazz(HashMap.class)
1086             .setGenerics(
1087                 Arrays.asList(
1088                     ConcreteReference.withClazz(String.class),
1089                     ConcreteReference.withClazz(Integer.class)))
1090             .build();
1091     Reference outerMapReference =
1092         ConcreteReference.builder()
1093             .setClazz(HashMap.class)
1094             .setGenerics(Arrays.asList(mapReference, mapReference))
1095             .build();
1096 
1097     Variable variable = Variable.builder().setType(TypeNode.INT).setName("anArg").build();
1098     VariableExpr varExpr = VariableExpr.builder().setVariable(variable).build();
1099 
1100     MethodInvocationExpr methodExpr =
1101         MethodInvocationExpr.builder()
1102             .setMethodName("foobar")
1103             .setGenerics(
1104                 Arrays.asList(
1105                     ConcreteReference.withClazz(String.class),
1106                     ConcreteReference.withClazz(Double.class),
1107                     TypeNode.WILDCARD_REFERENCE,
1108                     outerMapReference))
1109             .setArguments(Arrays.asList(varExpr, varExpr, varExpr))
1110             .setExprReferenceExpr(varExpr)
1111             .setReturnType(TypeNode.STRING)
1112             .build();
1113 
1114     Variable lhsVariable = Variable.builder().setType(TypeNode.STRING).setName("someStr").build();
1115     VariableExpr lhsVarExpr =
1116         VariableExpr.builder().setVariable(lhsVariable).setIsDecl(true).setIsFinal(true).build();
1117 
1118     AssignmentExpr assignExpr =
1119         AssignmentExpr.builder().setVariableExpr(lhsVarExpr).setValueExpr(methodExpr).build();
1120 
1121     assignExpr.accept(writerVisitor);
1122     assertEquals(
1123         "final String someStr = anArg.<String, Double, ?, HashMap<HashMap<String, Integer>,"
1124             + " HashMap<String, Integer>>>foobar(anArg, anArg, anArg)",
1125         writerVisitor.write());
1126   }
1127 
1128   @Test
writeMethodInvocationExpr_chained()1129   public void writeMethodInvocationExpr_chained() {
1130     Variable variable = Variable.builder().setType(TypeNode.INT).setName("libraryClient").build();
1131     VariableExpr varExpr = VariableExpr.builder().setVariable(variable).build();
1132 
1133     MethodInvocationExpr firstMethodExpr =
1134         MethodInvocationExpr.builder()
1135             .setMethodName("streamBooksCallable")
1136             .setExprReferenceExpr(varExpr)
1137             .build();
1138     MethodInvocationExpr secondMethodExpr =
1139         MethodInvocationExpr.builder()
1140             .setMethodName("doAnotherThing")
1141             .setExprReferenceExpr(firstMethodExpr)
1142             .build();
1143     MethodInvocationExpr methodExpr =
1144         MethodInvocationExpr.builder()
1145             .setMethodName("call")
1146             .setExprReferenceExpr(secondMethodExpr)
1147             .build();
1148 
1149     methodExpr.accept(writerVisitor);
1150     assertEquals(
1151         "libraryClient.streamBooksCallable().doAnotherThing().call()", writerVisitor.write());
1152   }
1153 
1154   @Test
writeCastExpr_basic()1155   public void writeCastExpr_basic() {
1156     Variable variable = Variable.builder().setType(TypeNode.STRING).setName("str").build();
1157     VariableExpr varExpr = VariableExpr.builder().setVariable(variable).build();
1158     CastExpr castExpr =
1159         CastExpr.builder()
1160             .setType(TypeNode.withReference(ConcreteReference.withClazz(Object.class)))
1161             .setExpr(varExpr)
1162             .build();
1163     castExpr.accept(writerVisitor);
1164     assertEquals("((Object) str)", writerVisitor.write());
1165   }
1166 
1167   @Test
writeCastExpr_methodInvocation()1168   public void writeCastExpr_methodInvocation() {
1169     TypeNode someType =
1170         TypeNode.withReference(
1171             VaporReference.builder()
1172                 .setName("SomeClass")
1173                 .setPakkage("com.google.api.some.pakkage")
1174                 .build());
1175 
1176     MethodInvocationExpr methodExpr =
1177         MethodInvocationExpr.builder()
1178             .setMethodName("foobar")
1179             .setStaticReferenceType(someType)
1180             .setReturnType(TypeNode.STRING)
1181             .build();
1182     CastExpr castExpr =
1183         CastExpr.builder()
1184             .setType(TypeNode.withReference(ConcreteReference.withClazz(Object.class)))
1185             .setExpr(methodExpr)
1186             .build();
1187     castExpr.accept(writerVisitor);
1188     assertEquals("((Object) SomeClass.foobar())", writerVisitor.write());
1189   }
1190 
1191   @Test
writeCastExpr_nested()1192   public void writeCastExpr_nested() {
1193     Variable variable = Variable.builder().setType(TypeNode.STRING).setName("str").build();
1194     VariableExpr varExpr = VariableExpr.builder().setVariable(variable).build();
1195     CastExpr castExpr =
1196         CastExpr.builder()
1197             .setType(TypeNode.withReference(ConcreteReference.withClazz(Object.class)))
1198             .setExpr(varExpr)
1199             .build();
1200     castExpr = CastExpr.builder().setType(TypeNode.STRING).setExpr(castExpr).build();
1201     castExpr.accept(writerVisitor);
1202     assertEquals("((String) ((Object) str))", writerVisitor.write());
1203   }
1204 
1205   @Test
writeAnonymousClassExpr_basic()1206   public void writeAnonymousClassExpr_basic() {
1207     ConcreteReference ref = ConcreteReference.withClazz(Runnable.class);
1208     TypeNode type = TypeNode.withReference(ref);
1209     AssignmentExpr assignmentExpr = createAssignmentExpr("foobar", "false", TypeNode.BOOLEAN);
1210     ExprStatement statement = ExprStatement.withExpr(assignmentExpr);
1211     MethodDefinition method =
1212         MethodDefinition.builder()
1213             .setScope(ScopeNode.PUBLIC)
1214             .setReturnType(TypeNode.VOID)
1215             .setName("run")
1216             .setIsOverride(true)
1217             .setBody(Arrays.asList(statement))
1218             .build();
1219 
1220     AnonymousClassExpr anonymousClassExpr =
1221         AnonymousClassExpr.builder().setType(type).setMethods(Arrays.asList(method)).build();
1222     anonymousClassExpr.accept(writerVisitor);
1223     assertEquals(
1224         LineFormatter.lines(
1225             "new Runnable() {\n",
1226             "@Override\n",
1227             "public void run() {\n",
1228             "boolean foobar = false;\n}\n\n}"),
1229         writerVisitor.write());
1230   }
1231 
1232   @Test
writeAnonymousClassExpr_withStatementsMethods()1233   public void writeAnonymousClassExpr_withStatementsMethods() {
1234     ConcreteReference ref = ConcreteReference.withClazz(Runnable.class);
1235     TypeNode type = TypeNode.withReference(ref);
1236     // [Constructing] private static final String s = "foo";
1237     Variable variable = createVariable("s", TypeNode.STRING);
1238     VariableExpr variableExpr =
1239         VariableExpr.builder()
1240             .setScope(ScopeNode.PRIVATE)
1241             .setIsDecl(true)
1242             .setIsFinal(true)
1243             .setIsStatic(true)
1244             .setVariable(variable)
1245             .build();
1246     ValueExpr valueExpr = ValueExpr.builder().setValue(StringObjectValue.withValue("foo")).build();
1247     AssignmentExpr assignmentExpr =
1248         AssignmentExpr.builder().setVariableExpr(variableExpr).setValueExpr(valueExpr).build();
1249     ExprStatement exprStatement = ExprStatement.withExpr(assignmentExpr);
1250 
1251     MethodDefinition methodDefinition =
1252         MethodDefinition.builder()
1253             .setName("run")
1254             .setIsOverride(true)
1255             .setScope(ScopeNode.PUBLIC)
1256             .setReturnType(TypeNode.VOID)
1257             .setBody(
1258                 Arrays.asList(ExprStatement.withExpr(createAssignmentExpr("x", "3", TypeNode.INT))))
1259             .build();
1260     AnonymousClassExpr anonymousClassExpr =
1261         AnonymousClassExpr.builder()
1262             .setType(type)
1263             .setStatements(Arrays.asList(exprStatement))
1264             .setMethods(Arrays.asList(methodDefinition))
1265             .build();
1266     anonymousClassExpr.accept(writerVisitor);
1267     String expected =
1268         LineFormatter.lines(
1269             "new Runnable() {\n",
1270             "private static final String s = \"foo\";\n",
1271             "@Override\n",
1272             "public void run() {\n",
1273             "int x = 3;\n}\n\n}");
1274     assertEquals(expected, writerVisitor.write());
1275   }
1276 
1277   @Test
writeAnonymousClassExpr_generics()1278   public void writeAnonymousClassExpr_generics() {
1279     // [Constructing] Function<List<IOException>, MethodDefinition>
1280     ConcreteReference exceptionListRef =
1281         ConcreteReference.builder()
1282             .setClazz(List.class)
1283             .setGenerics(Arrays.asList(ConcreteReference.withClazz(IOException.class)))
1284             .build();
1285     ConcreteReference methodDefinitionRef = ConcreteReference.withClazz(MethodDefinition.class);
1286     ConcreteReference ref =
1287         ConcreteReference.builder()
1288             .setClazz(Function.class)
1289             .setGenerics(Arrays.asList(exceptionListRef, methodDefinitionRef))
1290             .build();
1291     TypeNode type = TypeNode.withReference(ref);
1292     // [Constructing] an input argument whose type is `List<IOException>`
1293     VariableExpr arg =
1294         VariableExpr.builder()
1295             .setVariable(
1296                 Variable.builder()
1297                     .setName("arg")
1298                     .setType(TypeNode.withReference(exceptionListRef))
1299                     .build())
1300             .setIsDecl(true)
1301             .build();
1302     // [Constructing] a return variable expression whose type is `MethodDefinition`
1303     VariableExpr returnArg =
1304         VariableExpr.builder()
1305             .setVariable(
1306                 Variable.builder()
1307                     .setName("returnArg")
1308                     .setType(TypeNode.withReference(methodDefinitionRef))
1309                     .build())
1310             .build();
1311     MethodDefinition method =
1312         MethodDefinition.builder()
1313             .setScope(ScopeNode.PUBLIC)
1314             .setReturnType(TypeNode.withReference(methodDefinitionRef))
1315             .setArguments(Arrays.asList(arg))
1316             .setIsOverride(true)
1317             .setReturnExpr(returnArg)
1318             .setName("apply")
1319             .build();
1320     AnonymousClassExpr anonymousClassExpr =
1321         AnonymousClassExpr.builder().setType(type).setMethods(Arrays.asList(method)).build();
1322     anonymousClassExpr.accept(writerVisitor);
1323     String expected =
1324         LineFormatter.lines(
1325             "new Function<List<IOException>, MethodDefinition>() {\n",
1326             "@Override\n",
1327             "public MethodDefinition apply(List<IOException> arg) {\n",
1328             "return returnArg;\n",
1329             "}\n\n}");
1330     assertEquals(expected, writerVisitor.write());
1331   }
1332 
1333   @Test
writeThrowExpr_basic()1334   public void writeThrowExpr_basic() {
1335     TypeNode npeType =
1336         TypeNode.withReference(ConcreteReference.withClazz(NullPointerException.class));
1337     ThrowExpr throwExpr = ThrowExpr.builder().setType(npeType).build();
1338     throwExpr.accept(writerVisitor);
1339     assertEquals("throw new NullPointerException()", writerVisitor.write());
1340   }
1341 
1342   @Test
writeThrowExpr_basicThrowExpr()1343   public void writeThrowExpr_basicThrowExpr() {
1344     Expr exprToThrow =
1345         MethodInvocationExpr.builder()
1346             .setStaticReferenceType(
1347                 TypeNode.withReference(ConcreteReference.withClazz(Statement.class)))
1348             .setMethodName("createException")
1349             .setReturnType(TypeNode.withExceptionClazz(Exception.class))
1350             .build();
1351 
1352     ThrowExpr throwExpr = ThrowExpr.builder().setThrowExpr(exprToThrow).build();
1353     throwExpr.accept(writerVisitor);
1354     assertEquals("throw Statement.createException()", writerVisitor.write());
1355   }
1356 
1357   @Test
writeThrowExpr_basicWithMessage()1358   public void writeThrowExpr_basicWithMessage() {
1359     TypeNode npeType =
1360         TypeNode.withReference(ConcreteReference.withClazz(NullPointerException.class));
1361     String message = "Some message asdf";
1362     ThrowExpr throwExpr = ThrowExpr.builder().setType(npeType).setMessageExpr(message).build();
1363     throwExpr.accept(writerVisitor);
1364     assertEquals("throw new NullPointerException(\"Some message asdf\")", writerVisitor.write());
1365   }
1366 
1367   @Test
writeThrowExpr_basicWithCause()1368   public void writeThrowExpr_basicWithCause() {
1369     TypeNode npeType =
1370         TypeNode.withReference(ConcreteReference.withClazz(NullPointerException.class));
1371     ThrowExpr throwExpr =
1372         ThrowExpr.builder()
1373             .setType(npeType)
1374             .setCauseExpr(
1375                 NewObjectExpr.builder()
1376                     .setType(TypeNode.withReference(ConcreteReference.withClazz(Throwable.class)))
1377                     .build())
1378             .build();
1379     throwExpr.accept(writerVisitor);
1380     assertEquals("throw new NullPointerException(new Throwable())", writerVisitor.write());
1381   }
1382 
1383   @Test
writeThrowExpr_messageExpr()1384   public void writeThrowExpr_messageExpr() {
1385     TypeNode npeType = TypeNode.withExceptionClazz(NullPointerException.class);
1386     Expr messageExpr =
1387         MethodInvocationExpr.builder()
1388             .setMethodName("foobar")
1389             .setReturnType(TypeNode.STRING)
1390             .build();
1391     ThrowExpr throwExpr = ThrowExpr.builder().setType(npeType).setMessageExpr(messageExpr).build();
1392 
1393     throwExpr.accept(writerVisitor);
1394     assertEquals("throw new NullPointerException(foobar())", writerVisitor.write());
1395   }
1396 
1397   @Test
writeThrowExpr_messageAndCauseExpr()1398   public void writeThrowExpr_messageAndCauseExpr() {
1399     TypeNode npeType = TypeNode.withExceptionClazz(NullPointerException.class);
1400     Expr messageExpr =
1401         MethodInvocationExpr.builder()
1402             .setMethodName("foobar")
1403             .setReturnType(TypeNode.STRING)
1404             .build();
1405     ThrowExpr throwExpr =
1406         ThrowExpr.builder()
1407             .setType(npeType)
1408             .setMessageExpr(messageExpr)
1409             .setCauseExpr(
1410                 NewObjectExpr.builder()
1411                     .setType(TypeNode.withReference(ConcreteReference.withClazz(Throwable.class)))
1412                     .build())
1413             .build();
1414 
1415     throwExpr.accept(writerVisitor);
1416     assertEquals(
1417         "throw new NullPointerException(foobar(), new Throwable())", writerVisitor.write());
1418   }
1419 
1420   @Test
writeInstanceofExpr()1421   public void writeInstanceofExpr() {
1422     Variable variable = Variable.builder().setName("x").setType(TypeNode.STRING).build();
1423     VariableExpr variableExpr = VariableExpr.builder().setVariable(variable).build();
1424     InstanceofExpr instanceofExpr =
1425         InstanceofExpr.builder().setCheckType(TypeNode.STRING).setExpr(variableExpr).build();
1426     instanceofExpr.accept(writerVisitor);
1427     assertEquals("x instanceof String", writerVisitor.write());
1428   }
1429 
1430   @Test
writeEnumRefExpr_basic()1431   public void writeEnumRefExpr_basic() {
1432     TypeNode enumType =
1433         TypeNode.withReference(
1434             ConcreteReference.builder()
1435                 .setClazz(TypeNode.TypeKind.class)
1436                 .setIsStaticImport(true)
1437                 .build());
1438     EnumRefExpr enumRefExpr = EnumRefExpr.builder().setName("VOID").setType(enumType).build();
1439 
1440     enumRefExpr.accept(writerVisitor);
1441     assertEquals("TypeKind.VOID", writerVisitor.write());
1442   }
1443 
1444   @Test
writeEnumRefExpr_nested()1445   public void writeEnumRefExpr_nested() {
1446     TypeNode enumType =
1447         TypeNode.withReference(ConcreteReference.withClazz(TypeNode.TypeKind.class));
1448     EnumRefExpr enumRefExpr = EnumRefExpr.builder().setName("VOID").setType(enumType).build();
1449     enumRefExpr.accept(writerVisitor);
1450     assertEquals("TypeNode.TypeKind.VOID", writerVisitor.write());
1451   }
1452 
1453   @Test
writeReturnExpr_basic()1454   public void writeReturnExpr_basic() {
1455     ReturnExpr returnExpr =
1456         ReturnExpr.withExpr(ValueExpr.withValue(StringObjectValue.withValue("asdf")));
1457     returnExpr.accept(writerVisitor);
1458     assertEquals("return \"asdf\"", writerVisitor.write());
1459   }
1460 
1461   /** =============================== STATEMENTS =============================== */
1462   @Test
writeExprStatement()1463   public void writeExprStatement() {
1464     TypeNode someType =
1465         TypeNode.withReference(
1466             VaporReference.builder()
1467                 .setName("SomeClass")
1468                 .setPakkage("com.google.api.some.pakkage")
1469                 .build());
1470 
1471     MethodInvocationExpr methodExpr =
1472         MethodInvocationExpr.builder()
1473             .setMethodName("foobar")
1474             .setStaticReferenceType(someType)
1475             .build();
1476     ExprStatement exprStatement = ExprStatement.withExpr(methodExpr);
1477 
1478     exprStatement.accept(writerVisitor);
1479     assertEquals("SomeClass.foobar();\n", writerVisitor.write());
1480   }
1481 
1482   @Test
writeBlockStatement_empty()1483   public void writeBlockStatement_empty() {
1484     BlockStatement blockStatement = BlockStatement.builder().build();
1485     blockStatement.accept(writerVisitor);
1486     assertEquals("{\n}\n", writerVisitor.write());
1487   }
1488 
1489   @Test
writeBlockStatement_simple()1490   public void writeBlockStatement_simple() {
1491     TypeNode someType =
1492         TypeNode.withReference(
1493             VaporReference.builder()
1494                 .setName("SomeClass")
1495                 .setPakkage("com.google.api.some.pakkage")
1496                 .build());
1497 
1498     MethodInvocationExpr methodExpr =
1499         MethodInvocationExpr.builder()
1500             .setMethodName("foobar")
1501             .setStaticReferenceType(someType)
1502             .build();
1503     BlockStatement blockStatement =
1504         BlockStatement.builder().setBody(Arrays.asList(ExprStatement.withExpr(methodExpr))).build();
1505 
1506     blockStatement.accept(writerVisitor);
1507     assertEquals("{\nSomeClass.foobar();\n}\n", writerVisitor.write());
1508   }
1509 
1510   @Test
writeBlockStatement_static()1511   public void writeBlockStatement_static() {
1512     TypeNode someType =
1513         TypeNode.withReference(
1514             VaporReference.builder()
1515                 .setName("SomeClass")
1516                 .setPakkage("com.google.api.some.pakkage")
1517                 .build());
1518 
1519     MethodInvocationExpr methodExpr =
1520         MethodInvocationExpr.builder()
1521             .setMethodName("foobar")
1522             .setStaticReferenceType(someType)
1523             .build();
1524     BlockStatement blockStatement =
1525         BlockStatement.builder()
1526             .setIsStatic(true)
1527             .setBody(
1528                 Arrays.asList(
1529                     ExprStatement.withExpr(methodExpr), ExprStatement.withExpr(methodExpr)))
1530             .build();
1531 
1532     blockStatement.accept(writerVisitor);
1533     assertEquals("static {\nSomeClass.foobar();\nSomeClass.foobar();\n}\n", writerVisitor.write());
1534   }
1535 
1536   @Test
writeIfStatement_simple()1537   public void writeIfStatement_simple() {
1538     AssignmentExpr assignExpr = createAssignmentExpr("x", "3", TypeNode.INT);
1539     Statement assignExprStatement = ExprStatement.withExpr(assignExpr);
1540     List<Statement> ifBody = Arrays.asList(assignExprStatement, assignExprStatement);
1541     VariableExpr condExpr = createVariableExpr("condition", TypeNode.BOOLEAN);
1542 
1543     IfStatement ifStatement =
1544         IfStatement.builder().setConditionExpr(condExpr).setBody(ifBody).build();
1545 
1546     ifStatement.accept(writerVisitor);
1547     assertEquals(
1548         LineFormatter.lines("if (condition) {\n", "int x = 3;\n", "int x = 3;\n", "}\n"),
1549         writerVisitor.write());
1550   }
1551 
1552   @Test
writeIfStatement_withElse()1553   public void writeIfStatement_withElse() {
1554     AssignmentExpr assignExpr = createAssignmentExpr("x", "3", TypeNode.INT);
1555     Statement assignExprStatement = ExprStatement.withExpr(assignExpr);
1556     List<Statement> ifBody = Arrays.asList(assignExprStatement, assignExprStatement);
1557     VariableExpr condExpr = createVariableExpr("condition", TypeNode.BOOLEAN);
1558 
1559     IfStatement ifStatement =
1560         IfStatement.builder()
1561             .setConditionExpr(condExpr)
1562             .setBody(ifBody)
1563             .setElseBody(ifBody)
1564             .build();
1565 
1566     ifStatement.accept(writerVisitor);
1567     assertEquals(
1568         String.format(
1569             "%s%s%s" + "%s%s%s%s",
1570             "if (condition) {\n",
1571             "int x = 3;\n",
1572             "int x = 3;\n",
1573             "} else {\n",
1574             "int x = 3;\n",
1575             "int x = 3;\n",
1576             "}\n"),
1577         writerVisitor.write());
1578   }
1579 
1580   @Test
writeIfStatement_elseIfs()1581   public void writeIfStatement_elseIfs() {
1582     List<Statement> ifBody =
1583         Arrays.asList(
1584             ExprStatement.withExpr(createAssignmentExpr("x", "3", TypeNode.INT)),
1585             ExprStatement.withExpr(createAssignmentExpr("fooBar", "true", TypeNode.BOOLEAN)));
1586 
1587     VariableExpr condExprOne = createVariableExpr("condition", TypeNode.BOOLEAN);
1588     VariableExpr condExprTwo = createVariableExpr("fooBarCheck", TypeNode.BOOLEAN);
1589     VariableExpr condExprThree = createVariableExpr("anotherCondition", TypeNode.BOOLEAN);
1590     VariableExpr condExprFour = createVariableExpr("lookAtMe", TypeNode.BOOLEAN);
1591 
1592     IfStatement ifStatement =
1593         IfStatement.builder()
1594             .setConditionExpr(condExprOne)
1595             .setBody(ifBody)
1596             .addElseIf(condExprTwo, ifBody)
1597             .addElseIf(condExprThree, ifBody)
1598             .addElseIf(condExprFour, ifBody)
1599             .build();
1600 
1601     ifStatement.accept(writerVisitor);
1602     String expected =
1603         LineFormatter.lines(
1604             "if (condition) {\n",
1605             "int x = 3;\n",
1606             "boolean fooBar = true;\n",
1607             "} else if (fooBarCheck) {\n",
1608             "int x = 3;\n",
1609             "boolean fooBar = true;\n",
1610             "} else if (anotherCondition) {\n",
1611             "int x = 3;\n",
1612             "boolean fooBar = true;\n",
1613             "} else if (lookAtMe) {\n",
1614             "int x = 3;\n",
1615             "boolean fooBar = true;\n",
1616             "}\n");
1617 
1618     assertEquals(expected, writerVisitor.write());
1619   }
1620 
1621   @Test
writeIfStatement_nested()1622   public void writeIfStatement_nested() {
1623     List<Statement> ifBody =
1624         Arrays.asList(
1625             ExprStatement.withExpr(createAssignmentExpr("x", "3", TypeNode.INT)),
1626             ExprStatement.withExpr(createAssignmentExpr("fooBar", "true", TypeNode.BOOLEAN)));
1627 
1628     VariableExpr condExprOne = createVariableExpr("condition", TypeNode.BOOLEAN);
1629     VariableExpr condExprTwo = createVariableExpr("fooBarCheck", TypeNode.BOOLEAN);
1630     VariableExpr condExprThree = createVariableExpr("anotherCondition", TypeNode.BOOLEAN);
1631     VariableExpr condExprFour = createVariableExpr("lookAtMe", TypeNode.BOOLEAN);
1632 
1633     IfStatement nestedTwoIfStatement =
1634         IfStatement.builder()
1635             .setConditionExpr(condExprThree)
1636             .setBody(ifBody)
1637             .setElseBody(ifBody)
1638             .build();
1639     IfStatement nestedOneIfStatement =
1640         IfStatement.builder()
1641             .setConditionExpr(condExprTwo)
1642             .setBody(
1643                 Arrays.asList(
1644                     ExprStatement.withExpr(createAssignmentExpr("anInt", "10", TypeNode.INT)),
1645                     nestedTwoIfStatement))
1646             .build();
1647     IfStatement nestedZeroIfStatement =
1648         IfStatement.builder()
1649             .setConditionExpr(condExprOne)
1650             .setBody(Arrays.asList(nestedOneIfStatement))
1651             .addElseIf(condExprFour, ifBody)
1652             .build();
1653 
1654     IfStatement ifStatement =
1655         IfStatement.builder()
1656             .setConditionExpr(condExprOne)
1657             .setBody(Arrays.asList(nestedZeroIfStatement))
1658             .build();
1659 
1660     ifStatement.accept(writerVisitor);
1661 
1662     String expected =
1663         LineFormatter.lines(
1664             "if (condition) {\n",
1665             "if (condition) {\n",
1666             "if (fooBarCheck) {\n",
1667             "int anInt = 10;\n",
1668             "if (anotherCondition) {\n",
1669             "int x = 3;\n",
1670             "boolean fooBar = true;\n",
1671             "} else {\n",
1672             "int x = 3;\n",
1673             "boolean fooBar = true;\n",
1674             "}\n",
1675             "}\n",
1676             "} else if (lookAtMe) {\n",
1677             "int x = 3;\n",
1678             "boolean fooBar = true;\n",
1679             "}\n",
1680             "}\n");
1681     assertEquals(expected, writerVisitor.write());
1682   }
1683 
1684   @Test
writeWhileStatement_simple()1685   public void writeWhileStatement_simple() {
1686     AssignmentExpr assignExpr = createAssignmentExpr("x", "3", TypeNode.INT);
1687     Statement assignExprStatement = ExprStatement.withExpr(assignExpr);
1688     List<Statement> whileBody = Arrays.asList(assignExprStatement, assignExprStatement);
1689     VariableExpr condExpr = createVariableExpr("condition", TypeNode.BOOLEAN);
1690 
1691     WhileStatement whileStatement =
1692         WhileStatement.builder().setConditionExpr(condExpr).setBody(whileBody).build();
1693 
1694     whileStatement.accept(writerVisitor);
1695     assertEquals(
1696         LineFormatter.lines("while (condition) {\n", "int x = 3;\n", "int x = 3;\n", "}\n"),
1697         writerVisitor.write());
1698   }
1699 
1700   @Test
writeForStatement()1701   public void writeForStatement() {
1702     AssignmentExpr assignExpr = createAssignmentExpr("x", "3", TypeNode.INT);
1703     Statement assignExprStatement = ExprStatement.withExpr(assignExpr);
1704     List<Statement> body = Arrays.asList(assignExprStatement, assignExprStatement);
1705 
1706     VariableExpr varDeclExpr = createVariableDeclExpr("str", TypeNode.STRING);
1707     Expr collectionExpr = MethodInvocationExpr.builder().setMethodName("getSomeStrings").build();
1708 
1709     ForStatement forStatement =
1710         ForStatement.builder()
1711             .setLocalVariableExpr(varDeclExpr)
1712             .setCollectionExpr(collectionExpr)
1713             .setBody(body)
1714             .build();
1715 
1716     forStatement.accept(writerVisitor);
1717     assertEquals(
1718         String.format(
1719             "%s%s%s%s",
1720             "for (String str : getSomeStrings()) {\n", "int x = 3;\n", "int x = 3;\n", "}\n"),
1721         writerVisitor.write());
1722   }
1723 
1724   @Test
writeGeneralForStatement_basicIsDecl()1725   public void writeGeneralForStatement_basicIsDecl() {
1726     AssignmentExpr assignExpr = createAssignmentExpr("x", "3", TypeNode.INT);
1727     Statement assignExprStatement = ExprStatement.withExpr(assignExpr);
1728     List<Statement> body = Arrays.asList(assignExprStatement, assignExprStatement);
1729 
1730     VariableExpr localVarExpr = createVariableDeclExpr("i", TypeNode.INT);
1731     ValueExpr initValueExpr =
1732         ValueExpr.withValue(PrimitiveValue.builder().setValue("0").setType(TypeNode.INT).build());
1733     Expr maxSizeExpr =
1734         MethodInvocationExpr.builder().setMethodName("maxSize").setReturnType(TypeNode.INT).build();
1735 
1736     GeneralForStatement forStatement =
1737         GeneralForStatement.incrementWith(localVarExpr, initValueExpr, maxSizeExpr, body);
1738 
1739     forStatement.accept(writerVisitor);
1740     assertEquals(
1741         String.format(
1742             "%s%s%s%s",
1743             "for (int i = 0; i < maxSize(); i++) {\n", "int x = 3;\n", "int x = 3;\n", "}\n"),
1744         writerVisitor.write());
1745   }
1746 
1747   @Test
writeGeneralForStatement_basicIsNotDecl()1748   public void writeGeneralForStatement_basicIsNotDecl() {
1749     AssignmentExpr assignExpr = createAssignmentExpr("x", "3", TypeNode.INT);
1750     Statement assignExprStatement = ExprStatement.withExpr(assignExpr);
1751     List<Statement> body = Arrays.asList(assignExprStatement, assignExprStatement);
1752 
1753     VariableExpr localVarExpr = createVariableExpr("i", TypeNode.INT);
1754     ValueExpr initValueExpr =
1755         ValueExpr.withValue(PrimitiveValue.builder().setValue("1").setType(TypeNode.INT).build());
1756     ValueExpr maxValueExpr =
1757         ValueExpr.withValue(PrimitiveValue.builder().setValue("10").setType(TypeNode.INT).build());
1758 
1759     GeneralForStatement forStatement =
1760         GeneralForStatement.incrementWith(localVarExpr, initValueExpr, maxValueExpr, body);
1761 
1762     forStatement.accept(writerVisitor);
1763     assertEquals(
1764         String.format(
1765             "%s%s%s%s", "for (i = 1; i < 10; i++) {\n", "int x = 3;\n", "int x = 3;\n", "}\n"),
1766         writerVisitor.write());
1767   }
1768 
1769   @Test
writeTryCatchStatement_simple()1770   public void writeTryCatchStatement_simple() {
1771     Reference exceptionReference = ConcreteReference.withClazz(IllegalArgumentException.class);
1772     TypeNode type = TypeNode.withReference(exceptionReference);
1773     VariableExpr variableExpr =
1774         VariableExpr.builder().setVariable(createVariable("e", type)).setIsDecl(true).build();
1775 
1776     TryCatchStatement tryCatch =
1777         TryCatchStatement.builder()
1778             .setTryBody(
1779                 Arrays.asList(ExprStatement.withExpr(createAssignmentExpr("x", "3", TypeNode.INT))))
1780             .addCatch(variableExpr, Collections.emptyList())
1781             .build();
1782 
1783     tryCatch.accept(writerVisitor);
1784     assertEquals(
1785         String.format(
1786             "%s%s%s%s",
1787             "try {\n", "int x = 3;\n", "} catch (IllegalArgumentException e) {\n", "}\n"),
1788         writerVisitor.write());
1789   }
1790 
1791   @Test
writeTryCatchStatement_simpleMultiCatch()1792   public void writeTryCatchStatement_simpleMultiCatch() {
1793     VariableExpr firstCatchVarExpr =
1794         VariableExpr.builder()
1795             .setVariable(
1796                 createVariable("e", TypeNode.withExceptionClazz(IllegalArgumentException.class)))
1797             .build();
1798     VariableExpr secondCatchVarExpr =
1799         VariableExpr.builder()
1800             .setVariable(createVariable("e", TypeNode.withExceptionClazz(RuntimeException.class)))
1801             .build();
1802 
1803     TryCatchStatement tryCatch =
1804         TryCatchStatement.builder()
1805             .setTryBody(
1806                 Arrays.asList(ExprStatement.withExpr(createAssignmentExpr("x", "3", TypeNode.INT))))
1807             .addCatch(
1808                 firstCatchVarExpr.toBuilder().setIsDecl(true).build(), Collections.emptyList())
1809             .addCatch(
1810                 secondCatchVarExpr.toBuilder().setIsDecl(true).build(), Collections.emptyList())
1811             .build();
1812 
1813     tryCatch.accept(writerVisitor);
1814     assertEquals(
1815         LineFormatter.lines(
1816             "try {\n",
1817             "int x = 3;\n",
1818             "} catch (IllegalArgumentException e) {\n",
1819             "} catch (RuntimeException e) {\n",
1820             "}\n"),
1821         writerVisitor.write());
1822   }
1823 
1824   @Test
writeTryCatchStatement_simpleMultiCatchOrderMatters()1825   public void writeTryCatchStatement_simpleMultiCatchOrderMatters() {
1826     VariableExpr firstCatchVarExpr =
1827         VariableExpr.builder()
1828             .setVariable(
1829                 createVariable("e", TypeNode.withExceptionClazz(IllegalArgumentException.class)))
1830             .build();
1831     VariableExpr secondCatchVarExpr =
1832         VariableExpr.builder()
1833             .setVariable(createVariable("e", TypeNode.withExceptionClazz(RuntimeException.class)))
1834             .build();
1835 
1836     TryCatchStatement tryCatch =
1837         TryCatchStatement.builder()
1838             .setTryBody(
1839                 Arrays.asList(ExprStatement.withExpr(createAssignmentExpr("x", "3", TypeNode.INT))))
1840             .addCatch(
1841                 secondCatchVarExpr.toBuilder().setIsDecl(true).build(), Collections.emptyList())
1842             .addCatch(
1843                 firstCatchVarExpr.toBuilder().setIsDecl(true).build(), Collections.emptyList())
1844             .build();
1845 
1846     tryCatch.accept(writerVisitor);
1847     assertEquals(
1848         LineFormatter.lines(
1849             "try {\n",
1850             "int x = 3;\n",
1851             "} catch (RuntimeException e) {\n",
1852             "} catch (IllegalArgumentException e) {\n",
1853             "}\n"),
1854         writerVisitor.write());
1855   }
1856 
1857   @Test
writeTryCatchStatement_withResources()1858   public void writeTryCatchStatement_withResources() {
1859     Reference exceptionReference = ConcreteReference.withClazz(IllegalArgumentException.class);
1860     TypeNode type = TypeNode.withReference(exceptionReference);
1861     VariableExpr variableExpr =
1862         VariableExpr.builder().setVariable(createVariable("e", type)).setIsDecl(true).build();
1863 
1864     TryCatchStatement tryCatch =
1865         TryCatchStatement.builder()
1866             .setTryResourceExpr(createAssignmentExpr("aBool", "false", TypeNode.BOOLEAN))
1867             .setTryBody(
1868                 Arrays.asList(ExprStatement.withExpr(createAssignmentExpr("y", "4", TypeNode.INT))))
1869             .addCatch(
1870                 variableExpr,
1871                 Arrays.asList(
1872                     ExprStatement.withExpr(createAssignmentExpr("foobar", "123", TypeNode.INT))))
1873             .build();
1874 
1875     tryCatch.accept(writerVisitor);
1876     assertEquals(
1877         String.format(
1878             "%s%s%s%s%s",
1879             "try (boolean aBool = false) {\n",
1880             "int y = 4;\n",
1881             "} catch (IllegalArgumentException e) {\n",
1882             "int foobar = 123;\n",
1883             "}\n"),
1884         writerVisitor.write());
1885   }
1886 
1887   @Test
writeTryCatchStatement_sampleCodeNoCatch()1888   public void writeTryCatchStatement_sampleCodeNoCatch() {
1889     TryCatchStatement tryCatch =
1890         TryCatchStatement.builder()
1891             .setTryBody(
1892                 Arrays.asList(ExprStatement.withExpr(createAssignmentExpr("x", "3", TypeNode.INT))))
1893             .setIsSampleCode(true)
1894             .build();
1895 
1896     tryCatch.accept(writerVisitor);
1897     assertEquals(LineFormatter.lines("try {\n", "int x = 3;\n", "}\n"), writerVisitor.write());
1898   }
1899 
1900   @Test
writeTryCatchStatement_sampleCodeWithCatch()1901   public void writeTryCatchStatement_sampleCodeWithCatch() {
1902     Reference exceptionReference = ConcreteReference.withClazz(IllegalArgumentException.class);
1903     TypeNode type = TypeNode.withReference(exceptionReference);
1904     VariableExpr variableExpr =
1905         VariableExpr.builder().setVariable(createVariable("e", type)).setIsDecl(true).build();
1906 
1907     TryCatchStatement tryCatch =
1908         TryCatchStatement.builder()
1909             .setIsSampleCode(true)
1910             .setTryResourceExpr(createAssignmentExpr("aBool", "false", TypeNode.BOOLEAN))
1911             .setTryBody(
1912                 Arrays.asList(ExprStatement.withExpr(createAssignmentExpr("y", "4", TypeNode.INT))))
1913             .addCatch(
1914                 variableExpr,
1915                 Arrays.asList(
1916                     ExprStatement.withExpr(createAssignmentExpr("foobar", "123", TypeNode.INT))))
1917             .build();
1918 
1919     tryCatch.accept(writerVisitor);
1920     assertEquals(
1921         String.format(
1922             "%s%s%s%s%s",
1923             "try (boolean aBool = false) {\n",
1924             "int y = 4;\n",
1925             "} catch (IllegalArgumentException e) {\n",
1926             "int foobar = 123;\n",
1927             "}\n"),
1928         writerVisitor.write());
1929   }
1930 
1931   @Test
writeSynchronizedStatement_basicThis()1932   public void writeSynchronizedStatement_basicThis() {
1933     SynchronizedStatement synchronizedStatement =
1934         SynchronizedStatement.builder()
1935             .setLock(
1936                 ThisObjectValue.withType(
1937                     TypeNode.withReference(ConcreteReference.withClazz(Expr.class))))
1938             .setBody(
1939                 ExprStatement.withExpr(
1940                     MethodInvocationExpr.builder().setMethodName("doStuff").build()))
1941             .build();
1942     synchronizedStatement.accept(writerVisitor);
1943     assertEquals(
1944         LineFormatter.lines("synchronized (this) {\n", "doStuff();\n", "}\n"),
1945         writerVisitor.write());
1946   }
1947 
1948   @Test
writeSynchronizedStatement_basicVariableExpr()1949   public void writeSynchronizedStatement_basicVariableExpr() {
1950     VariableExpr strVarExpr =
1951         VariableExpr.withVariable(
1952             Variable.builder().setName("str").setType(TypeNode.STRING).build());
1953 
1954     SynchronizedStatement synchronizedStatement =
1955         SynchronizedStatement.builder()
1956             .setLock(strVarExpr)
1957             .setBody(
1958                 ExprStatement.withExpr(
1959                     MethodInvocationExpr.builder().setMethodName("doStuff").build()))
1960             .build();
1961     synchronizedStatement.accept(writerVisitor);
1962     assertEquals(
1963         LineFormatter.lines("synchronized (str) {\n", "doStuff();\n", "}\n"),
1964         writerVisitor.write());
1965   }
1966 
1967   @Test
writeMethodDefinition_basic()1968   public void writeMethodDefinition_basic() {
1969     MethodDefinition methodDefinition =
1970         MethodDefinition.builder()
1971             .setName("close")
1972             .setScope(ScopeNode.PUBLIC)
1973             .setReturnType(TypeNode.VOID)
1974             .setBody(
1975                 Arrays.asList(ExprStatement.withExpr(createAssignmentExpr("x", "3", TypeNode.INT))))
1976             .build();
1977 
1978     methodDefinition.accept(writerVisitor);
1979     assertEquals(
1980         LineFormatter.lines("public void close() {\n", "int x = 3;\n", "}\n\n"),
1981         writerVisitor.write());
1982   }
1983 
1984   @Test
writeMethodDefinition_constructor()1985   public void writeMethodDefinition_constructor() {
1986     TypeNode returnType =
1987         TypeNode.withReference(
1988             VaporReference.builder()
1989                 .setName("LibrarySettings")
1990                 .setPakkage("com.google.example.library.v1")
1991                 .build());
1992     MethodDefinition methodDefinition =
1993         MethodDefinition.constructorBuilder()
1994             .setScope(ScopeNode.PUBLIC)
1995             .setReturnType(returnType)
1996             .build();
1997 
1998     methodDefinition.accept(writerVisitor);
1999     assertEquals("public LibrarySettings() {\n}\n\n", writerVisitor.write());
2000   }
2001 
2002   @Test
writeMethodDefinition_basicEmptyBody()2003   public void writeMethodDefinition_basicEmptyBody() {
2004     MethodDefinition methodDefinition =
2005         MethodDefinition.builder()
2006             .setName("close")
2007             .setScope(ScopeNode.PUBLIC)
2008             .setReturnType(TypeNode.VOID)
2009             .build();
2010 
2011     methodDefinition.accept(writerVisitor);
2012     assertEquals("public void close() {\n}\n\n", writerVisitor.write());
2013   }
2014 
2015   @Test
writeMethodDefinition_basicAbstract()2016   public void writeMethodDefinition_basicAbstract() {
2017     MethodDefinition methodDefinition =
2018         MethodDefinition.builder()
2019             .setName("close")
2020             .setIsAbstract(true)
2021             .setScope(ScopeNode.PUBLIC)
2022             .setReturnType(TypeNode.VOID)
2023             .setBody(
2024                 Arrays.asList(ExprStatement.withExpr(createAssignmentExpr("x", "3", TypeNode.INT))))
2025             .build();
2026 
2027     methodDefinition.accept(writerVisitor);
2028     assertEquals(
2029         LineFormatter.lines("public abstract void close() {\n", "int x = 3;\n", "}\n\n"),
2030         writerVisitor.write());
2031   }
2032 
2033   @Test
writeMethodDefinition_basicAbstractEmptyBody()2034   public void writeMethodDefinition_basicAbstractEmptyBody() {
2035     MethodDefinition methodDefinition =
2036         MethodDefinition.builder()
2037             .setName("close")
2038             .setIsAbstract(true)
2039             .setScope(ScopeNode.PUBLIC)
2040             .setReturnType(TypeNode.VOID)
2041             .build();
2042 
2043     methodDefinition.accept(writerVisitor);
2044     assertEquals("public abstract void close();\n", writerVisitor.write());
2045   }
2046 
2047   @Test
writeMethodDefinition_withArgumentsAndReturnExpr()2048   public void writeMethodDefinition_withArgumentsAndReturnExpr() {
2049     ValueExpr returnExpr =
2050         ValueExpr.builder()
2051             .setValue(PrimitiveValue.builder().setType(TypeNode.INT).setValue("3").build())
2052             .build();
2053     List<VariableExpr> arguments =
2054         Arrays.asList(
2055             VariableExpr.builder()
2056                 .setVariable(createVariable("x", TypeNode.INT))
2057                 .setIsDecl(true)
2058                 .build(),
2059             VariableExpr.builder()
2060                 .setVariable(createVariable("y", TypeNode.INT))
2061                 .setIsDecl(true)
2062                 .build());
2063     MethodDefinition methodDefinition =
2064         MethodDefinition.builder()
2065             .setName("close")
2066             .setScope(ScopeNode.PUBLIC)
2067             .setReturnType(TypeNode.INT)
2068             .setArguments(arguments)
2069             .setReturnExpr(returnExpr)
2070             .setBody(
2071                 Arrays.asList(
2072                     ExprStatement.withExpr(
2073                         createAssignmentExpr("foobar", "false", TypeNode.BOOLEAN))))
2074             .build();
2075 
2076     methodDefinition.accept(writerVisitor);
2077     assertEquals(
2078         LineFormatter.lines(
2079             "public int close(int x, int y) {\n",
2080             "boolean foobar = false;\n",
2081             "return 3;\n",
2082             "}\n\n"),
2083         writerVisitor.write());
2084   }
2085 
2086   @Test
writeMethodDefinition_withCommentsAnnotationsAndThrows()2087   public void writeMethodDefinition_withCommentsAnnotationsAndThrows() {
2088     LineComment lineComment = LineComment.withComment("AUTO-GENERATED DOCUMENTATION AND METHOD");
2089     JavaDocComment javaDocComment =
2090         JavaDocComment.builder()
2091             .addComment("This is an override method called `close()`")
2092             .addParam("valOne", "string type")
2093             .addParam("valTwo", "boolean type")
2094             .addComment("The return value is int 3.")
2095             .build();
2096     ValueExpr returnExpr =
2097         ValueExpr.builder()
2098             .setValue(PrimitiveValue.builder().setType(TypeNode.INT).setValue("3").build())
2099             .build();
2100     List<VariableExpr> arguments =
2101         Arrays.asList(
2102             VariableExpr.builder()
2103                 .setVariable(createVariable("valOne", TypeNode.STRING))
2104                 .setIsDecl(true)
2105                 .build(),
2106             VariableExpr.builder()
2107                 .setVariable(createVariable("valTwo", TypeNode.BOOLEAN))
2108                 .setIsDecl(true)
2109                 .build());
2110     MethodDefinition methodDefinition =
2111         MethodDefinition.builder()
2112             .setName("close")
2113             .setIsOverride(true)
2114             .setIsFinal(true)
2115             .setIsStatic(true)
2116             .setScope(ScopeNode.PROTECTED)
2117             .setReturnType(TypeNode.INT)
2118             .setThrowsExceptions(
2119                 Arrays.asList(
2120                     TypeNode.withExceptionClazz(IOException.class),
2121                     TypeNode.withExceptionClazz(TimeoutException.class),
2122                     TypeNode.withExceptionClazz(InterruptedException.class)))
2123             .setArguments(arguments)
2124             .setReturnExpr(returnExpr)
2125             .setHeaderCommentStatements(
2126                 Arrays.asList(
2127                     CommentStatement.withComment(lineComment),
2128                     CommentStatement.withComment(javaDocComment)))
2129             .setAnnotations(
2130                 Arrays.asList(
2131                     AnnotationNode.withSuppressWarnings("all"), AnnotationNode.DEPRECATED))
2132             .setBody(
2133                 Arrays.asList(
2134                     createForStatement(),
2135                     ExprStatement.withExpr(
2136                         createAssignmentExpr("foobar", "false", TypeNode.BOOLEAN))))
2137             .build();
2138 
2139     methodDefinition.accept(writerVisitor);
2140     String expected =
2141         LineFormatter.lines(
2142             "// AUTO-GENERATED DOCUMENTATION AND METHOD\n",
2143             "/**\n",
2144             "* This is an override method called `close()`\n",
2145             "* The return value is int 3.\n",
2146             "* @param valOne string type\n",
2147             "* @param valTwo boolean type\n",
2148             "*/\n",
2149             "@SuppressWarnings(\"all\")\n",
2150             "@Deprecated\n",
2151             "@Override\n",
2152             "protected static final int close(String valOne, boolean valTwo) throws"
2153                 + " IOException, TimeoutException, InterruptedException {\n",
2154             "for (String str : getSomeStrings()) {\n",
2155             "boolean aBool = false;\n",
2156             "}\n",
2157             "boolean foobar = false;\n",
2158             "return 3;\n",
2159             "}\n\n");
2160     assertEquals(expected, writerVisitor.write());
2161   }
2162 
2163   @Test
writeMethodDefinition_templatedReturnTypeAndArguments()2164   public void writeMethodDefinition_templatedReturnTypeAndArguments() {
2165     Reference mapRef = ConcreteReference.withClazz(Map.class);
2166     List<VariableExpr> arguments =
2167         Arrays.asList(
2168             VariableExpr.builder()
2169                 .setVariable(createVariable("x", TypeNode.withReference(mapRef)))
2170                 .setIsDecl(true)
2171                 .setTemplateObjects(Arrays.asList("K", TypeNode.STRING))
2172                 .build(),
2173             VariableExpr.builder()
2174                 .setVariable(createVariable("y", TypeNode.withReference(mapRef)))
2175                 .setIsDecl(true)
2176                 .setTemplateObjects(Arrays.asList("T", "V"))
2177                 .build());
2178 
2179     TypeNode returnType = TypeNode.withReference(mapRef);
2180     MethodDefinition methodDefinition =
2181         MethodDefinition.builder()
2182             .setName("close")
2183             .setScope(ScopeNode.PUBLIC)
2184             .setReturnType(returnType)
2185             .setTemplateNames(Arrays.asList("T", "K", "V"))
2186             .setReturnTemplateNames(Arrays.asList("K", "V"))
2187             .setArguments(arguments)
2188             .setReturnExpr(
2189                 MethodInvocationExpr.builder()
2190                     .setMethodName("foobar")
2191                     .setReturnType(returnType)
2192                     .build())
2193             .build();
2194 
2195     methodDefinition.accept(writerVisitor);
2196     assertEquals(
2197         LineFormatter.lines(
2198             "public <T, K, V> Map<K, V> close(Map<K, String> x, Map<T, V> y) {\n",
2199             "return foobar();\n",
2200             "}\n\n"),
2201         writerVisitor.write());
2202   }
2203 
2204   @Test
writeClassDefinition_basicWithFileHeader()2205   public void writeClassDefinition_basicWithFileHeader() {
2206     List<CommentStatement> fileHeader =
2207         Arrays.asList(CommentStatement.withComment(BlockComment.withComment("Apache License")));
2208     ClassDefinition classDef =
2209         ClassDefinition.builder()
2210             .setFileHeader(fileHeader)
2211             .setPackageString("com.google.example.library.v1.stub")
2212             .setName("LibraryServiceStub")
2213             .setScope(ScopeNode.PUBLIC)
2214             .build();
2215 
2216     classDef.accept(writerVisitor);
2217     assertEquals(
2218         LineFormatter.lines(
2219             "/*\n",
2220             " * Apache License\n",
2221             " */\n\n",
2222             "package com.google.example.library.v1.stub;\n",
2223             "\n",
2224             "public class LibraryServiceStub {}\n"),
2225         writerVisitor.write());
2226   }
2227 
2228   @Test
writeClassDefinition_withAnnotationsExtendsAndImplements()2229   public void writeClassDefinition_withAnnotationsExtendsAndImplements() {
2230     ClassDefinition classDef =
2231         ClassDefinition.builder()
2232             .setPackageString("com.google.example.library.v1.stub")
2233             .setName("LibraryServiceStub")
2234             .setScope(ScopeNode.PUBLIC)
2235             .setIsFinal(true)
2236             .setAnnotations(
2237                 Arrays.asList(
2238                     AnnotationNode.DEPRECATED, AnnotationNode.withSuppressWarnings("all")))
2239             .setExtendsType(TypeNode.STRING)
2240             .setImplementsTypes(
2241                 Arrays.asList(
2242                     TypeNode.withReference(ConcreteReference.withClazz(Appendable.class)),
2243                     TypeNode.withReference(ConcreteReference.withClazz(Cloneable.class)),
2244                     TypeNode.withReference(ConcreteReference.withClazz(Readable.class))))
2245             .build();
2246 
2247     classDef.accept(writerVisitor);
2248     assertEquals(
2249         LineFormatter.lines(
2250             "package com.google.example.library.v1.stub;\n",
2251             "\n",
2252             "@Deprecated\n",
2253             "@SuppressWarnings(\"all\")\n",
2254             "public final class LibraryServiceStub extends String implements Appendable,"
2255                 + " Cloneable, Readable {}\n"),
2256         writerVisitor.write());
2257   }
2258 
2259   @Test
writeClassDefinition_commentsStatementsAndMethods()2260   public void writeClassDefinition_commentsStatementsAndMethods() {
2261     LineComment lineComment = LineComment.withComment("AUTO-GENERATED DOCUMENTATION AND CLASS");
2262     JavaDocComment javaDocComment =
2263         JavaDocComment.builder()
2264             .addComment("Class to configure an instance of {@link LibraryServiceStub}.")
2265             .addParagraph("The default instance has everything set to sensible defaults:")
2266             .addUnorderedList(
2267                 Arrays.asList(
2268                     "The default service address (library-example.googleapis.com) and default port"
2269                         + " (1234) are used.",
2270                     "Credentials are acquired automatically through Application Default"
2271                         + " Credentials.",
2272                     "Retries are configured for idempotent methods but not for non-idempotent"
2273                         + " methods."))
2274             .build();
2275     List<Reference> subGenerics =
2276         Arrays.asList(
2277             ConcreteReference.withClazz(String.class),
2278             ConcreteReference.withClazz(MethodDefinition.class));
2279     Reference mapEntryReference =
2280         ConcreteReference.builder().setClazz(Map.Entry.class).setGenerics(subGenerics).build();
2281     List<Reference> generics =
2282         Arrays.asList(ConcreteReference.withClazz(ClassDefinition.class), mapEntryReference);
2283     Reference mapReference =
2284         ConcreteReference.builder().setClazz(Map.class).setGenerics(generics).build();
2285 
2286     List<Statement> statements =
2287         Arrays.asList(
2288             ExprStatement.withExpr(
2289                 VariableExpr.builder()
2290                     .setVariable(
2291                         createVariable(
2292                             "x",
2293                             TypeNode.withReference(
2294                                 ConcreteReference.withClazz(AssignmentExpr.class))))
2295                     .setIsDecl(true)
2296                     .setScope(ScopeNode.PRIVATE)
2297                     .build()),
2298             ExprStatement.withExpr(
2299                 VariableExpr.builder()
2300                     .setVariable(createVariable("y", TypeNode.withReference(mapReference)))
2301                     .setIsDecl(true)
2302                     .setScope(ScopeNode.PROTECTED)
2303                     .build()));
2304 
2305     MethodDefinition methodOne =
2306         MethodDefinition.builder()
2307             .setName("open")
2308             .setScope(ScopeNode.PUBLIC)
2309             .setReturnType(TypeNode.BOOLEAN)
2310             .setReturnExpr(
2311                 ValueExpr.builder()
2312                     .setValue(
2313                         PrimitiveValue.builder().setType(TypeNode.BOOLEAN).setValue("true").build())
2314                     .build())
2315             .build();
2316 
2317     MethodDefinition methodTwo =
2318         MethodDefinition.builder()
2319             .setName("close")
2320             .setScope(ScopeNode.PUBLIC)
2321             .setReturnType(TypeNode.VOID)
2322             .setBody(
2323                 Arrays.asList(
2324                     ExprStatement.withExpr(
2325                         createAssignmentExpr("foobar", "false", TypeNode.BOOLEAN))))
2326             .build();
2327 
2328     List<MethodDefinition> methods = Arrays.asList(methodOne, methodTwo);
2329 
2330     ClassDefinition nestedClassDef =
2331         ClassDefinition.builder()
2332             .setName("IAmANestedClass")
2333             .setIsNested(true)
2334             .setScope(ScopeNode.PRIVATE)
2335             .setIsStatic(true)
2336             .setMethods(Arrays.asList(methodOne))
2337             .build();
2338 
2339     ClassDefinition classDef =
2340         ClassDefinition.builder()
2341             .setPackageString("com.google.example.library.v1.stub")
2342             .setHeaderCommentStatements(
2343                 Arrays.asList(
2344                     CommentStatement.withComment(lineComment),
2345                     CommentStatement.withComment(javaDocComment)))
2346             .setName("LibraryServiceStub")
2347             .setScope(ScopeNode.PUBLIC)
2348             .setStatements(statements)
2349             .setMethods(methods)
2350             .setNestedClasses(Arrays.asList(nestedClassDef))
2351             .build();
2352 
2353     classDef.accept(writerVisitor);
2354     String expected =
2355         LineFormatter.lines(
2356             "package com.google.example.library.v1.stub;\n",
2357             "\n",
2358             "import com.google.api.generator.engine.ast.AssignmentExpr;\n",
2359             "import com.google.api.generator.engine.ast.ClassDefinition;\n",
2360             "import com.google.api.generator.engine.ast.MethodDefinition;\n",
2361             "import java.util.Map;\n",
2362             "\n",
2363             "// AUTO-GENERATED DOCUMENTATION AND CLASS\n",
2364             "/**\n",
2365             " * Class to configure an instance of {{@literal @}link LibraryServiceStub}.\n",
2366             " *\n",
2367             " * <p>The default instance has everything set to sensible defaults:\n",
2368             " *\n",
2369             " * <ul>\n",
2370             " *   <li>The default service address (library-example.googleapis.com) and default"
2371                 + " port (1234) are\n",
2372             " *       used.\n",
2373             " *   <li>Credentials are acquired automatically through Application Default"
2374                 + " Credentials.\n",
2375             " *   <li>Retries are configured for idempotent methods but not for non-idempotent"
2376                 + " methods.\n",
2377             " * </ul>\n",
2378             " */\n",
2379             "public class LibraryServiceStub {\n",
2380             "  private AssignmentExpr x;\n",
2381             "  protected Map<ClassDefinition, Map.Entry<String, MethodDefinition>> y;\n\n",
2382             "  public boolean open() {\n",
2383             "    return true;\n",
2384             "  }\n\n",
2385             "  public void close() {\n",
2386             "    boolean foobar = false;\n",
2387             "  }\n",
2388             "\n",
2389             "  private static class IAmANestedClass {\n\n",
2390             "    public boolean open() {\n",
2391             "      return true;\n",
2392             "    }\n",
2393             "  }\n",
2394             "}\n");
2395     assertEquals(expected, writerVisitor.write());
2396   }
2397 
2398   @Test
writeReferenceConstructorExpr_thisConstructorWithArguments()2399   public void writeReferenceConstructorExpr_thisConstructorWithArguments() {
2400     VaporReference ref =
2401         VaporReference.builder().setName("Student").setPakkage("com.google.example.v1").build();
2402     TypeNode classType = TypeNode.withReference(ref);
2403     VariableExpr idVarExpr =
2404         VariableExpr.builder()
2405             .setVariable(Variable.builder().setName("id").setType(TypeNode.STRING).build())
2406             .build();
2407     VariableExpr nameVarExpr =
2408         VariableExpr.builder()
2409             .setVariable(Variable.builder().setName("name").setType(TypeNode.STRING).build())
2410             .build();
2411     ReferenceConstructorExpr referenceConstructorExpr =
2412         ReferenceConstructorExpr.thisBuilder()
2413             .setArguments(Arrays.asList(idVarExpr, nameVarExpr))
2414             .setType(classType)
2415             .build();
2416     referenceConstructorExpr.accept(writerVisitor);
2417     assertThat(writerVisitor.write()).isEqualTo("this(id, name)");
2418   }
2419 
2420   @Test
writeReferenceConstructorExpr_superConstructorWithNoArguments()2421   public void writeReferenceConstructorExpr_superConstructorWithNoArguments() {
2422     VaporReference ref =
2423         VaporReference.builder().setName("Parent").setPakkage("com.google.example.v1").build();
2424     TypeNode classType = TypeNode.withReference(ref);
2425     ReferenceConstructorExpr referenceConstructorExpr =
2426         ReferenceConstructorExpr.superBuilder().setType(classType).build();
2427     referenceConstructorExpr.accept(writerVisitor);
2428     assertThat(writerVisitor.write()).isEqualTo("super()");
2429   }
2430 
2431   @Test
writeThisObjectValue_methodReturn()2432   public void writeThisObjectValue_methodReturn() {
2433     VaporReference ref =
2434         VaporReference.builder().setName("Student").setPakkage("com.google.example.v1").build();
2435     TypeNode classType = TypeNode.withReference(ref);
2436     MethodDefinition methodDefinition =
2437         MethodDefinition.builder()
2438             .setName("apply")
2439             .setScope(ScopeNode.PUBLIC)
2440             .setReturnType(TypeNode.withReference(ref))
2441             .setReturnExpr(
2442                 ValueExpr.builder().setValue(ThisObjectValue.withType(classType)).build())
2443             .build();
2444     methodDefinition.accept(writerVisitor);
2445     assertEquals(
2446         LineFormatter.lines("public Student apply() {\n", "return this;\n", "}\n\n"),
2447         writerVisitor.write());
2448   }
2449 
2450   @Test
writeThisObjectValue_accessFieldAndInvokeMethod()2451   public void writeThisObjectValue_accessFieldAndInvokeMethod() {
2452     VaporReference ref =
2453         VaporReference.builder().setName("Student").setPakkage("com.google.example.v1").build();
2454     TypeNode classType = TypeNode.withReference(ref);
2455     ThisObjectValue thisObjectValue = ThisObjectValue.withType(classType);
2456     ValueExpr thisValueExpr = ValueExpr.withValue(thisObjectValue);
2457     VariableExpr varExpr =
2458         VariableExpr.builder()
2459             .setVariable(Variable.builder().setName("id").setType(TypeNode.STRING).build())
2460             .build();
2461     Variable subVariable = Variable.builder().setName("name").setType(TypeNode.STRING).build();
2462     VariableExpr thisVariableExpr =
2463         VariableExpr.builder().setVariable(subVariable).setExprReferenceExpr(thisValueExpr).build();
2464 
2465     MethodInvocationExpr methodExpr =
2466         MethodInvocationExpr.builder()
2467             .setMethodName("getName")
2468             .setExprReferenceExpr(ValueExpr.withValue(thisObjectValue))
2469             .setArguments(Arrays.asList(varExpr))
2470             .setReturnType(TypeNode.STRING)
2471             .build();
2472     AssignmentExpr assignmentExpr =
2473         AssignmentExpr.builder().setVariableExpr(thisVariableExpr).setValueExpr(methodExpr).build();
2474 
2475     assignmentExpr.accept(writerVisitor);
2476     assertThat(writerVisitor.write()).isEqualTo("this.name = this.getName(id)");
2477   }
2478 
2479   @Test
writeSuperObjectValue_accessFieldAndInvokeMethod()2480   public void writeSuperObjectValue_accessFieldAndInvokeMethod() {
2481     VaporReference ref =
2482         VaporReference.builder().setName("Student").setPakkage("com.google.example.v1").build();
2483     TypeNode classType = TypeNode.withReference(ref);
2484     SuperObjectValue superObjectValue = SuperObjectValue.withType(classType);
2485     ValueExpr superValueExpr = ValueExpr.withValue(superObjectValue);
2486     Variable subVariable = Variable.builder().setName("name").setType(TypeNode.STRING).build();
2487     VariableExpr superVariableExpr =
2488         VariableExpr.builder()
2489             .setVariable(subVariable)
2490             .setExprReferenceExpr(superValueExpr)
2491             .build();
2492 
2493     MethodInvocationExpr methodExpr =
2494         MethodInvocationExpr.builder()
2495             .setMethodName("getName")
2496             .setExprReferenceExpr(ValueExpr.withValue(superObjectValue))
2497             .setReturnType(TypeNode.STRING)
2498             .build();
2499     AssignmentExpr assignmentExpr =
2500         AssignmentExpr.builder()
2501             .setVariableExpr(superVariableExpr)
2502             .setValueExpr(methodExpr)
2503             .build();
2504 
2505     assignmentExpr.accept(writerVisitor);
2506     assertThat(writerVisitor.write()).isEqualTo("super.name = super.getName()");
2507   }
2508 
2509   @Test
writeUnaryOperationExpr_postfixIncrement()2510   public void writeUnaryOperationExpr_postfixIncrement() {
2511     VariableExpr variableExpr =
2512         VariableExpr.withVariable(Variable.builder().setType(TypeNode.INT).setName("i").build());
2513     UnaryOperationExpr postIncrementOperationExpr =
2514         UnaryOperationExpr.postfixIncrementWithExpr(variableExpr);
2515     postIncrementOperationExpr.accept(writerVisitor);
2516     assertThat(writerVisitor.write()).isEqualTo("i++");
2517   }
2518 
2519   @Test
writeUnaryOperationExpr_logicalNot()2520   public void writeUnaryOperationExpr_logicalNot() {
2521     MethodInvocationExpr methodInvocationExpr =
2522         MethodInvocationExpr.builder()
2523             .setMethodName("isEmpty")
2524             .setReturnType(TypeNode.BOOLEAN)
2525             .build();
2526     UnaryOperationExpr logicalNotOperationExpr =
2527         UnaryOperationExpr.logicalNotWithExpr(methodInvocationExpr);
2528     logicalNotOperationExpr.accept(writerVisitor);
2529     assertThat(writerVisitor.write()).isEqualTo("!isEmpty()");
2530   }
2531 
2532   @Test
writeRelationalOperationExpr_equalTo()2533   public void writeRelationalOperationExpr_equalTo() {
2534     VariableExpr variableExprLHS =
2535         VariableExpr.withVariable(
2536             Variable.builder().setType(TypeNode.BOOLEAN_OBJECT).setName("isGood").build());
2537     MethodInvocationExpr methodInvocationExpr =
2538         MethodInvocationExpr.builder()
2539             .setMethodName("isBad")
2540             .setReturnType(TypeNode.BOOLEAN)
2541             .build();
2542 
2543     RelationalOperationExpr equalToOperationExpr =
2544         RelationalOperationExpr.equalToWithExprs(variableExprLHS, methodInvocationExpr);
2545     equalToOperationExpr.accept(writerVisitor);
2546     assertThat(writerVisitor.write()).isEqualTo("isGood == isBad()");
2547   }
2548 
2549   @Test
writeRelationOperationExpr_notEqualTo()2550   public void writeRelationOperationExpr_notEqualTo() {
2551     TypeNode someType =
2552         TypeNode.withReference(
2553             VaporReference.builder()
2554                 .setName("SomeClass")
2555                 .setPakkage("com.google.api.generator.engine")
2556                 .build());
2557     MethodInvocationExpr lhsExpr =
2558         MethodInvocationExpr.builder()
2559             .setMethodName("getName")
2560             .setStaticReferenceType(someType)
2561             .setReturnType(TypeNode.STRING)
2562             .build();
2563     ValueExpr rhsExpr = ValueExpr.createNullExpr();
2564 
2565     RelationalOperationExpr notEqualToOperationExpr =
2566         RelationalOperationExpr.notEqualToWithExprs(lhsExpr, rhsExpr);
2567     notEqualToOperationExpr.accept(writerVisitor);
2568     assertThat(writerVisitor.write()).isEqualTo("SomeClass.getName() != null");
2569   }
2570 
2571   @Test
writeRelationalOperationExpr_lessThan()2572   public void writeRelationalOperationExpr_lessThan() {
2573     VariableExpr lhsExpr = VariableExpr.withVariable(createVariable("i", TypeNode.INT));
2574     MethodInvocationExpr rhsExpr =
2575         MethodInvocationExpr.builder()
2576             .setMethodName("getMaxNumber")
2577             .setReturnType(TypeNode.INT)
2578             .build();
2579 
2580     RelationalOperationExpr lessThanWithExprs =
2581         RelationalOperationExpr.lessThanWithExprs(lhsExpr, rhsExpr);
2582     lessThanWithExprs.accept(writerVisitor);
2583     assertThat(writerVisitor.write()).isEqualTo("i < getMaxNumber()");
2584   }
2585 
2586   @Test
writeLogicalOperationExpr_logicalAnd()2587   public void writeLogicalOperationExpr_logicalAnd() {
2588     VariableExpr lhsExpr = VariableExpr.withVariable(createVariable("isEmpty", TypeNode.BOOLEAN));
2589     VaporReference ref =
2590         VaporReference.builder().setName("Student").setPakkage("com.google.example.v1").build();
2591     TypeNode classType = TypeNode.withReference(ref);
2592     MethodInvocationExpr rhsExpr =
2593         MethodInvocationExpr.builder()
2594             .setMethodName("isValid")
2595             .setExprReferenceExpr(ValueExpr.withValue(ThisObjectValue.withType(classType)))
2596             .setReturnType(TypeNode.BOOLEAN)
2597             .build();
2598     LogicalOperationExpr logicalOperationExpr =
2599         LogicalOperationExpr.logicalAndWithExprs(lhsExpr, rhsExpr);
2600     logicalOperationExpr.accept(writerVisitor);
2601     assertThat(writerVisitor.write()).isEqualTo("isEmpty && this.isValid()");
2602   }
2603 
2604   @Test
writeLogicalOperationExpr_logicalOr()2605   public void writeLogicalOperationExpr_logicalOr() {
2606     VariableExpr lhsExpr = VariableExpr.withVariable(createVariable("isGood", TypeNode.BOOLEAN));
2607     MethodInvocationExpr rhsExpr =
2608         MethodInvocationExpr.builder()
2609             .setMethodName("isValid")
2610             .setReturnType(TypeNode.BOOLEAN)
2611             .build();
2612     LogicalOperationExpr logicalOperationExpr =
2613         LogicalOperationExpr.logicalOrWithExprs(lhsExpr, rhsExpr);
2614     logicalOperationExpr.accept(writerVisitor);
2615     assertThat(writerVisitor.write()).isEqualTo("isGood || isValid()");
2616   }
2617 
2618   @Test
writeAssignmentOperationExpr_multiplyAssignment()2619   public void writeAssignmentOperationExpr_multiplyAssignment() {
2620     VariableExpr lhsExpr = createVariableExpr("h", TypeNode.INT);
2621     ValueExpr rhsExpr =
2622         ValueExpr.withValue(
2623             PrimitiveValue.builder().setType(TypeNode.INT).setValue("1000003").build());
2624     AssignmentOperationExpr assignmentOperationExpr =
2625         AssignmentOperationExpr.multiplyAssignmentWithExprs(lhsExpr, rhsExpr);
2626     assignmentOperationExpr.accept(writerVisitor);
2627     assertThat(writerVisitor.write()).isEqualTo("h *= 1000003");
2628   }
2629 
2630   @Test
writeAssignmentOperationExpr_xorAssignment()2631   public void writeAssignmentOperationExpr_xorAssignment() {
2632     VariableExpr lhsExpr = createVariableExpr("h", TypeNode.INT);
2633     TypeNode objectType =
2634         TypeNode.withReference(
2635             VaporReference.builder().setName("Objects").setPakkage("java.lang.Object").build());
2636     MethodInvocationExpr rhsExpr =
2637         MethodInvocationExpr.builder()
2638             .setReturnType(TypeNode.INT)
2639             .setMethodName("hashCode")
2640             .setStaticReferenceType(objectType)
2641             .setArguments(
2642                 Arrays.asList(
2643                     VariableExpr.withVariable(createVariable("fixedValue", TypeNode.OBJECT))))
2644             .build();
2645     AssignmentOperationExpr assignmentOperationExpr =
2646         AssignmentOperationExpr.xorAssignmentWithExprs(lhsExpr, rhsExpr);
2647     assignmentOperationExpr.accept(writerVisitor);
2648     assertThat(writerVisitor.write()).isEqualTo("h ^= Objects.hashCode(fixedValue)");
2649   }
2650 
2651   @Test
writeLambdaExpr_noParameters()2652   public void writeLambdaExpr_noParameters() {
2653     LambdaExpr lambdaExpr =
2654         LambdaExpr.builder()
2655             .setReturnExpr(ValueExpr.withValue(StringObjectValue.withValue("foo")))
2656             .build();
2657     lambdaExpr.accept(writerVisitor);
2658     assertEquals("() -> \"foo\"", writerVisitor.write());
2659   }
2660 
2661   @Test
writeLambdaExpr_assignToVariable()2662   public void writeLambdaExpr_assignToVariable() {
2663     LambdaExpr lambdaExpr =
2664         LambdaExpr.builder()
2665             .setReturnExpr(ValueExpr.withValue(StringObjectValue.withValue("foo")))
2666             .build();
2667     AssignmentExpr assignmentExpr =
2668         AssignmentExpr.builder()
2669             .setVariableExpr(
2670                 VariableExpr.withVariable(
2671                     Variable.builder().setName("word").setType(TypeNode.STRING).build()))
2672             .setValueExpr(lambdaExpr)
2673             .build();
2674     assignmentExpr.accept(writerVisitor);
2675     assertEquals("word = () -> \"foo\"", writerVisitor.write());
2676   }
2677 
2678   @Test
writeLambdaExpr_oneParameter()2679   public void writeLambdaExpr_oneParameter() {
2680     VariableExpr argVarExpr =
2681         VariableExpr.builder()
2682             .setVariable(Variable.builder().setName("arg").setType(TypeNode.INT).build())
2683             .setIsDecl(true)
2684             .build();
2685 
2686     LambdaExpr lambdaExpr =
2687         LambdaExpr.builder()
2688             .setArguments(argVarExpr)
2689             .setReturnExpr(ValueExpr.withValue(StringObjectValue.withValue("foo")))
2690             .build();
2691     lambdaExpr.accept(writerVisitor);
2692     assertEquals("arg -> \"foo\"", writerVisitor.write());
2693   }
2694 
2695   @Test
writeLambdaExpr_severalParameters()2696   public void writeLambdaExpr_severalParameters() {
2697     VariableExpr argOneVarExpr =
2698         VariableExpr.builder()
2699             .setVariable(Variable.builder().setName("arg").setType(TypeNode.INT).build())
2700             .setIsDecl(true)
2701             .build();
2702     VariableExpr argTwoVarExpr =
2703         VariableExpr.builder()
2704             .setVariable(Variable.builder().setName("arg2").setType(TypeNode.STRING).build())
2705             .setIsDecl(true)
2706             .build();
2707     VariableExpr argThreeVarExpr =
2708         VariableExpr.builder()
2709             .setVariable(Variable.builder().setName("arg3").setType(TypeNode.BOOLEAN).build())
2710             .setIsDecl(true)
2711             .build();
2712 
2713     LambdaExpr lambdaExpr =
2714         LambdaExpr.builder()
2715             .setArguments(argOneVarExpr, argTwoVarExpr, argThreeVarExpr)
2716             .setReturnExpr(ValueExpr.withValue(StringObjectValue.withValue("foo")))
2717             .build();
2718     lambdaExpr.accept(writerVisitor);
2719     assertEquals("(int arg, String arg2, boolean arg3) -> \"foo\"", writerVisitor.write());
2720   }
2721 
2722   @Test
writeLambdaExpr_body()2723   public void writeLambdaExpr_body() {
2724     VariableExpr argVarExpr =
2725         VariableExpr.builder()
2726             .setVariable(Variable.builder().setName("arg").setType(TypeNode.INT).build())
2727             .build();
2728     VariableExpr fooVarExpr =
2729         VariableExpr.builder()
2730             .setVariable(Variable.builder().setName("foo").setType(TypeNode.INT).build())
2731             .build();
2732 
2733     ExprStatement statement =
2734         ExprStatement.withExpr(
2735             AssignmentExpr.builder()
2736                 .setVariableExpr(fooVarExpr.toBuilder().setIsDecl(true).build())
2737                 .setValueExpr(
2738                     ValueExpr.builder()
2739                         .setValue(
2740                             PrimitiveValue.builder().setType(TypeNode.INT).setValue("1").build())
2741                         .build())
2742                 .build());
2743 
2744     LambdaExpr lambdaExpr =
2745         LambdaExpr.builder()
2746             .setArguments(argVarExpr.toBuilder().setIsDecl(true).build())
2747             .setReturnExpr(ValueExpr.withValue(StringObjectValue.withValue("foo")))
2748             .setBody(Arrays.asList(statement))
2749             .build();
2750     lambdaExpr.accept(writerVisitor);
2751     assertEquals("arg -> {\nint foo = 1;\nreturn \"foo\";\n}", writerVisitor.write());
2752   }
2753 
2754   @Test
writeEmptyLineStatement()2755   public void writeEmptyLineStatement() {
2756     EmptyLineStatement statement = EmptyLineStatement.create();
2757     statement.accept(writerVisitor);
2758     assertEquals("\n", writerVisitor.write());
2759   }
2760 
2761   @Test
writeBreakStatement()2762   public void writeBreakStatement() {
2763     BreakStatement statement = BreakStatement.create();
2764     statement.accept(writerVisitor);
2765     assertEquals("break;", writerVisitor.write());
2766   }
2767 
2768   @Test
writePackageInfoDefinition()2769   public void writePackageInfoDefinition() {
2770     PackageInfoDefinition packageInfo =
2771         PackageInfoDefinition.builder()
2772             .setPakkage("com.google.example.library.v1")
2773             .setAnnotations(
2774                 AnnotationNode.withType(
2775                     TypeNode.withReference(ConcreteReference.withClazz(Generated.class))))
2776             .setFileHeader(
2777                 CommentStatement.withComment(
2778                     BlockComment.withComment("Lorum ipsum dolor sit amet")))
2779             .setHeaderCommentStatements(
2780                 CommentStatement.withComment(
2781                     JavaDocComment.withComment("Consecteteur adipisping elit")))
2782             .build();
2783 
2784     packageInfo.accept(writerVisitor);
2785     assertEquals(
2786         LineFormatter.lines(
2787             "/*\n",
2788             " * Lorum ipsum dolor sit amet\n",
2789             " */\n",
2790             "\n",
2791             "/** Consecteteur adipisping elit */\n",
2792             "@Generated\n",
2793             "package com.google.example.library.v1;\n",
2794             "\n",
2795             "import javax.annotation.Generated;\n"),
2796         writerVisitor.write());
2797   }
2798 
2799   /** =============================== HELPERS =============================== */
createAssignmentExpr( String variableName, String value, TypeNode type)2800   private static AssignmentExpr createAssignmentExpr(
2801       String variableName, String value, TypeNode type) {
2802     VariableExpr variableExpr = createVariableDeclExpr(variableName, type);
2803     Value val = PrimitiveValue.builder().setType(type).setValue(value).build();
2804     Expr valueExpr = ValueExpr.builder().setValue(val).build();
2805     return AssignmentExpr.builder().setVariableExpr(variableExpr).setValueExpr(valueExpr).build();
2806   }
2807 
createVariableExpr(String variableName, TypeNode type)2808   private static VariableExpr createVariableExpr(String variableName, TypeNode type) {
2809     return createVariableExpr(variableName, type, false);
2810   }
2811 
createVariableDeclExpr(String variableName, TypeNode type)2812   private static VariableExpr createVariableDeclExpr(String variableName, TypeNode type) {
2813     return createVariableExpr(variableName, type, true);
2814   }
2815 
createVariableExpr( String variableName, TypeNode type, boolean isDecl)2816   private static VariableExpr createVariableExpr(
2817       String variableName, TypeNode type, boolean isDecl) {
2818     return VariableExpr.builder()
2819         .setVariable(createVariable(variableName, type))
2820         .setIsDecl(isDecl)
2821         .build();
2822   }
2823 
createVariable(String variableName, TypeNode type)2824   private static Variable createVariable(String variableName, TypeNode type) {
2825     return Variable.builder().setName(variableName).setType(type).build();
2826   }
2827 
createForStatement()2828   private static ForStatement createForStatement() {
2829     Expr collectionExpr = MethodInvocationExpr.builder().setMethodName("getSomeStrings").build();
2830     ExprStatement assignExprStatement =
2831         ExprStatement.withExpr(createAssignmentExpr("aBool", "false", TypeNode.BOOLEAN));
2832     List<Statement> body = Arrays.asList(assignExprStatement);
2833     return ForStatement.builder()
2834         .setLocalVariableExpr(createVariableDeclExpr("str", TypeNode.STRING))
2835         .setCollectionExpr(collectionExpr)
2836         .setBody(body)
2837         .build();
2838   }
2839 
createSampleCode()2840   private static String createSampleCode() {
2841     JavaWriterVisitor writerVisitor = new JavaWriterVisitor();
2842     TryCatchStatement tryCatch =
2843         TryCatchStatement.builder()
2844             .setTryResourceExpr(createAssignmentExpr("condition", "false", TypeNode.BOOLEAN))
2845             .setTryBody(
2846                 Arrays.asList(ExprStatement.withExpr(createAssignmentExpr("x", "3", TypeNode.INT))))
2847             .setIsSampleCode(true)
2848             .build();
2849 
2850     tryCatch.accept(writerVisitor);
2851     return writerVisitor.write();
2852   }
2853 }
2854