• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 /*
2  * Copyright (C) 2015 Square, Inc.
3  *
4  * Licensed under the Apache License, Version 2.0 (the "License");
5  * you may not use this file except in compliance with the License.
6  * You may obtain a copy of the License at
7  *
8  * http://www.apache.org/licenses/LICENSE-2.0
9  *
10  * Unless required by applicable law or agreed to in writing, software
11  * distributed under the License is distributed on an "AS IS" BASIS,
12  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13  * See the License for the specific language governing permissions and
14  * limitations under the License.
15  */
16 package com.squareup.javapoet;
17 
18 import com.google.common.collect.ImmutableMap;
19 import com.google.testing.compile.CompilationRule;
20 import java.io.File;
21 import java.io.IOException;
22 import java.io.Serializable;
23 import java.math.BigDecimal;
24 import java.util.AbstractSet;
25 import java.util.ArrayList;
26 import java.util.Arrays;
27 import java.util.Collections;
28 import java.util.Comparator;
29 import java.util.EventListener;
30 import java.util.List;
31 import java.util.Locale;
32 import java.util.Map;
33 import java.util.Random;
34 import java.util.concurrent.Callable;
35 import javax.lang.model.element.Element;
36 import javax.lang.model.element.Modifier;
37 import javax.lang.model.element.TypeElement;
38 import javax.lang.model.type.TypeMirror;
39 import org.junit.Rule;
40 import org.junit.Test;
41 import org.junit.runner.RunWith;
42 import org.junit.runners.JUnit4;
43 import org.mockito.Mockito;
44 
45 import static com.google.common.truth.Truth.assertThat;
46 import static org.junit.Assert.assertEquals;
47 import static org.junit.Assert.fail;
48 
49 @RunWith(JUnit4.class)
50 public final class TypeSpecTest {
51   private final String tacosPackage = "com.squareup.tacos";
52   private static final String donutsPackage = "com.squareup.donuts";
53 
54   @Rule public final CompilationRule compilation = new CompilationRule();
55 
getElement(Class<?> clazz)56   private TypeElement getElement(Class<?> clazz) {
57     return compilation.getElements().getTypeElement(clazz.getCanonicalName());
58   }
59 
basic()60   @Test public void basic() throws Exception {
61     TypeSpec taco = TypeSpec.classBuilder("Taco")
62         .addMethod(MethodSpec.methodBuilder("toString")
63             .addAnnotation(Override.class)
64             .addModifiers(Modifier.PUBLIC, Modifier.FINAL)
65             .returns(String.class)
66             .addCode("return $S;\n", "taco")
67             .build())
68         .build();
69     assertThat(toString(taco)).isEqualTo(""
70         + "package com.squareup.tacos;\n"
71         + "\n"
72         + "import java.lang.Override;\n"
73         + "import java.lang.String;\n"
74         + "\n"
75         + "class Taco {\n"
76         + "  @Override\n"
77         + "  public final String toString() {\n"
78         + "    return \"taco\";\n"
79         + "  }\n"
80         + "}\n");
81     assertEquals(472949424, taco.hashCode()); // update expected number if source changes
82   }
83 
interestingTypes()84   @Test public void interestingTypes() throws Exception {
85     TypeName listOfAny = ParameterizedTypeName.get(
86         ClassName.get(List.class), WildcardTypeName.subtypeOf(Object.class));
87     TypeName listOfExtends = ParameterizedTypeName.get(
88         ClassName.get(List.class), WildcardTypeName.subtypeOf(Serializable.class));
89     TypeName listOfSuper = ParameterizedTypeName.get(ClassName.get(List.class),
90         WildcardTypeName.supertypeOf(String.class));
91     TypeSpec taco = TypeSpec.classBuilder("Taco")
92         .addField(listOfAny, "extendsObject")
93         .addField(listOfExtends, "extendsSerializable")
94         .addField(listOfSuper, "superString")
95         .build();
96     assertThat(toString(taco)).isEqualTo(""
97         + "package com.squareup.tacos;\n"
98         + "\n"
99         + "import java.io.Serializable;\n"
100         + "import java.lang.String;\n"
101         + "import java.util.List;\n"
102         + "\n"
103         + "class Taco {\n"
104         + "  List<?> extendsObject;\n"
105         + "\n"
106         + "  List<? extends Serializable> extendsSerializable;\n"
107         + "\n"
108         + "  List<? super String> superString;\n"
109         + "}\n");
110   }
111 
anonymousInnerClass()112   @Test public void anonymousInnerClass() throws Exception {
113     ClassName foo = ClassName.get(tacosPackage, "Foo");
114     ClassName bar = ClassName.get(tacosPackage, "Bar");
115     ClassName thingThang = ClassName.get(tacosPackage, "Thing", "Thang");
116     TypeName thingThangOfFooBar = ParameterizedTypeName.get(thingThang, foo, bar);
117     ClassName thung = ClassName.get(tacosPackage, "Thung");
118     ClassName simpleThung = ClassName.get(tacosPackage, "SimpleThung");
119     TypeName thungOfSuperBar = ParameterizedTypeName.get(thung, WildcardTypeName.supertypeOf(bar));
120     TypeName thungOfSuperFoo = ParameterizedTypeName.get(thung, WildcardTypeName.supertypeOf(foo));
121     TypeName simpleThungOfBar = ParameterizedTypeName.get(simpleThung, bar);
122 
123     ParameterSpec thungParameter = ParameterSpec.builder(thungOfSuperFoo, "thung")
124         .addModifiers(Modifier.FINAL)
125         .build();
126     TypeSpec aSimpleThung = TypeSpec.anonymousClassBuilder(CodeBlock.of("$N", thungParameter))
127         .superclass(simpleThungOfBar)
128         .addMethod(MethodSpec.methodBuilder("doSomething")
129             .addAnnotation(Override.class)
130             .addModifiers(Modifier.PUBLIC)
131             .addParameter(bar, "bar")
132             .addCode("/* code snippets */\n")
133             .build())
134         .build();
135     TypeSpec aThingThang = TypeSpec.anonymousClassBuilder("")
136         .superclass(thingThangOfFooBar)
137         .addMethod(MethodSpec.methodBuilder("call")
138             .addAnnotation(Override.class)
139             .addModifiers(Modifier.PUBLIC)
140             .returns(thungOfSuperBar)
141             .addParameter(thungParameter)
142             .addCode("return $L;\n", aSimpleThung)
143             .build())
144         .build();
145     TypeSpec taco = TypeSpec.classBuilder("Taco")
146         .addField(FieldSpec.builder(thingThangOfFooBar, "NAME")
147             .addModifiers(Modifier.STATIC, Modifier.FINAL, Modifier.FINAL)
148             .initializer("$L", aThingThang)
149             .build())
150         .build();
151 
152     assertThat(toString(taco)).isEqualTo(""
153         + "package com.squareup.tacos;\n"
154         + "\n"
155         + "import java.lang.Override;\n"
156         + "\n"
157         + "class Taco {\n"
158         + "  static final Thing.Thang<Foo, Bar> NAME = new Thing.Thang<Foo, Bar>() {\n"
159         + "    @Override\n"
160         + "    public Thung<? super Bar> call(final Thung<? super Foo> thung) {\n"
161         + "      return new SimpleThung<Bar>(thung) {\n"
162         + "        @Override\n"
163         + "        public void doSomething(Bar bar) {\n"
164         + "          /* code snippets */\n"
165         + "        }\n"
166         + "      };\n"
167         + "    }\n"
168         + "  };\n"
169         + "}\n");
170   }
171 
annotatedParameters()172   @Test public void annotatedParameters() throws Exception {
173     TypeSpec service = TypeSpec.classBuilder("Foo")
174         .addMethod(MethodSpec.constructorBuilder()
175             .addModifiers(Modifier.PUBLIC)
176             .addParameter(long.class, "id")
177             .addParameter(ParameterSpec.builder(String.class, "one")
178                 .addAnnotation(ClassName.get(tacosPackage, "Ping"))
179                 .build())
180             .addParameter(ParameterSpec.builder(String.class, "two")
181                 .addAnnotation(ClassName.get(tacosPackage, "Ping"))
182                 .build())
183             .addParameter(ParameterSpec.builder(String.class, "three")
184                 .addAnnotation(AnnotationSpec.builder(ClassName.get(tacosPackage, "Pong"))
185                     .addMember("value", "$S", "pong")
186                     .build())
187                 .build())
188             .addParameter(ParameterSpec.builder(String.class, "four")
189                 .addAnnotation(ClassName.get(tacosPackage, "Ping"))
190                 .build())
191             .addCode("/* code snippets */\n")
192             .build())
193         .build();
194 
195     assertThat(toString(service)).isEqualTo(""
196         + "package com.squareup.tacos;\n"
197         + "\n"
198         + "import java.lang.String;\n"
199         + "\n"
200         + "class Foo {\n"
201         + "  public Foo(long id, @Ping String one, @Ping String two, @Pong(\"pong\") String three,\n"
202         + "      @Ping String four) {\n"
203         + "    /* code snippets */\n"
204         + "  }\n"
205         + "}\n");
206   }
207 
208   /**
209    * We had a bug where annotations were preventing us from doing the right thing when resolving
210    * imports. https://github.com/square/javapoet/issues/422
211    */
annotationsAndJavaLangTypes()212   @Test public void annotationsAndJavaLangTypes() throws Exception {
213     ClassName freeRange = ClassName.get("javax.annotation", "FreeRange");
214     TypeSpec taco = TypeSpec.classBuilder("EthicalTaco")
215         .addField(ClassName.get(String.class)
216             .annotated(AnnotationSpec.builder(freeRange).build()), "meat")
217         .build();
218 
219     assertThat(toString(taco)).isEqualTo(""
220         + "package com.squareup.tacos;\n"
221         + "\n"
222         + "import java.lang.String;\n"
223         + "import javax.annotation.FreeRange;\n"
224         + "\n"
225         + "class EthicalTaco {\n"
226         + "  @FreeRange String meat;\n"
227         + "}\n");
228   }
229 
retrofitStyleInterface()230   @Test public void retrofitStyleInterface() throws Exception {
231     ClassName observable = ClassName.get(tacosPackage, "Observable");
232     ClassName fooBar = ClassName.get(tacosPackage, "FooBar");
233     ClassName thing = ClassName.get(tacosPackage, "Thing");
234     ClassName things = ClassName.get(tacosPackage, "Things");
235     ClassName map = ClassName.get("java.util", "Map");
236     ClassName string = ClassName.get("java.lang", "String");
237     ClassName headers = ClassName.get(tacosPackage, "Headers");
238     ClassName post = ClassName.get(tacosPackage, "POST");
239     ClassName body = ClassName.get(tacosPackage, "Body");
240     ClassName queryMap = ClassName.get(tacosPackage, "QueryMap");
241     ClassName header = ClassName.get(tacosPackage, "Header");
242     TypeSpec service = TypeSpec.interfaceBuilder("Service")
243         .addMethod(MethodSpec.methodBuilder("fooBar")
244             .addModifiers(Modifier.PUBLIC, Modifier.ABSTRACT)
245             .addAnnotation(AnnotationSpec.builder(headers)
246                 .addMember("value", "$S", "Accept: application/json")
247                 .addMember("value", "$S", "User-Agent: foobar")
248                 .build())
249             .addAnnotation(AnnotationSpec.builder(post)
250                 .addMember("value", "$S", "/foo/bar")
251                 .build())
252             .returns(ParameterizedTypeName.get(observable, fooBar))
253             .addParameter(ParameterSpec.builder(ParameterizedTypeName.get(things, thing), "things")
254                 .addAnnotation(body)
255                 .build())
256             .addParameter(ParameterSpec.builder(
257                 ParameterizedTypeName.get(map, string, string), "query")
258                 .addAnnotation(AnnotationSpec.builder(queryMap)
259                     .addMember("encodeValues", "false")
260                     .build())
261                 .build())
262             .addParameter(ParameterSpec.builder(string, "authorization")
263                 .addAnnotation(AnnotationSpec.builder(header)
264                     .addMember("value", "$S", "Authorization")
265                     .build())
266                 .build())
267             .build())
268         .build();
269 
270     assertThat(toString(service)).isEqualTo(""
271         + "package com.squareup.tacos;\n"
272         + "\n"
273         + "import java.lang.String;\n"
274         + "import java.util.Map;\n"
275         + "\n"
276         + "interface Service {\n"
277         + "  @Headers({\n"
278         + "      \"Accept: application/json\",\n"
279         + "      \"User-Agent: foobar\"\n"
280         + "  })\n"
281         + "  @POST(\"/foo/bar\")\n"
282         + "  Observable<FooBar> fooBar(@Body Things<Thing> things,\n"
283         + "      @QueryMap(encodeValues = false) Map<String, String> query,\n"
284         + "      @Header(\"Authorization\") String authorization);\n"
285         + "}\n");
286   }
287 
annotatedField()288   @Test public void annotatedField() throws Exception {
289     TypeSpec taco = TypeSpec.classBuilder("Taco")
290         .addField(FieldSpec.builder(String.class, "thing", Modifier.PRIVATE, Modifier.FINAL)
291             .addAnnotation(AnnotationSpec.builder(ClassName.get(tacosPackage, "JsonAdapter"))
292                 .addMember("value", "$T.class", ClassName.get(tacosPackage, "Foo"))
293                 .build())
294             .build())
295         .build();
296     assertThat(toString(taco)).isEqualTo(""
297         + "package com.squareup.tacos;\n"
298         + "\n"
299         + "import java.lang.String;\n"
300         + "\n"
301         + "class Taco {\n"
302         + "  @JsonAdapter(Foo.class)\n"
303         + "  private final String thing;\n"
304         + "}\n");
305   }
306 
annotatedClass()307   @Test public void annotatedClass() throws Exception {
308     ClassName someType = ClassName.get(tacosPackage, "SomeType");
309     TypeSpec taco = TypeSpec.classBuilder("Foo")
310         .addAnnotation(AnnotationSpec.builder(ClassName.get(tacosPackage, "Something"))
311             .addMember("hi", "$T.$N", someType, "FIELD")
312             .addMember("hey", "$L", 12)
313             .addMember("hello", "$S", "goodbye")
314             .build())
315         .addModifiers(Modifier.PUBLIC)
316         .build();
317     assertThat(toString(taco)).isEqualTo(""
318         + "package com.squareup.tacos;\n"
319         + "\n"
320         + "@Something(\n"
321         + "    hi = SomeType.FIELD,\n"
322         + "    hey = 12,\n"
323         + "    hello = \"goodbye\"\n"
324         + ")\n"
325         + "public class Foo {\n"
326         + "}\n");
327   }
328 
addAnnotationDisallowsNull()329   @Test public void addAnnotationDisallowsNull() {
330     try {
331       TypeSpec.classBuilder("Foo").addAnnotation((AnnotationSpec) null);
332       fail();
333     } catch (NullPointerException expected) {
334       assertThat(expected).hasMessageThat().isEqualTo("annotationSpec == null");
335     }
336     try {
337       TypeSpec.classBuilder("Foo").addAnnotation((ClassName) null);
338       fail();
339     } catch (NullPointerException expected) {
340       assertThat(expected).hasMessageThat().isEqualTo("type == null");
341     }
342     try {
343       TypeSpec.classBuilder("Foo").addAnnotation((Class<?>) null);
344       fail();
345     } catch (NullPointerException expected) {
346       assertThat(expected).hasMessageThat().isEqualTo("clazz == null");
347     }
348   }
349 
enumWithSubclassing()350   @Test public void enumWithSubclassing() throws Exception {
351     TypeSpec roshambo = TypeSpec.enumBuilder("Roshambo")
352         .addModifiers(Modifier.PUBLIC)
353         .addEnumConstant("ROCK", TypeSpec.anonymousClassBuilder("")
354             .addJavadoc("Avalanche!\n")
355             .build())
356         .addEnumConstant("PAPER", TypeSpec.anonymousClassBuilder("$S", "flat")
357             .addMethod(MethodSpec.methodBuilder("toString")
358                 .addAnnotation(Override.class)
359                 .addModifiers(Modifier.PUBLIC)
360                 .returns(String.class)
361                 .addCode("return $S;\n", "paper airplane!")
362                 .build())
363             .build())
364         .addEnumConstant("SCISSORS", TypeSpec.anonymousClassBuilder("$S", "peace sign")
365             .build())
366         .addField(String.class, "handPosition", Modifier.PRIVATE, Modifier.FINAL)
367         .addMethod(MethodSpec.constructorBuilder()
368             .addParameter(String.class, "handPosition")
369             .addCode("this.handPosition = handPosition;\n")
370             .build())
371         .addMethod(MethodSpec.constructorBuilder()
372             .addCode("this($S);\n", "fist")
373             .build())
374         .build();
375     assertThat(toString(roshambo)).isEqualTo(""
376         + "package com.squareup.tacos;\n"
377         + "\n"
378         + "import java.lang.Override;\n"
379         + "import java.lang.String;\n"
380         + "\n"
381         + "public enum Roshambo {\n"
382         + "  /**\n"
383         + "   * Avalanche!\n"
384         + "   */\n"
385         + "  ROCK,\n"
386         + "\n"
387         + "  PAPER(\"flat\") {\n"
388         + "    @Override\n"
389         + "    public String toString() {\n"
390         + "      return \"paper airplane!\";\n"
391         + "    }\n"
392         + "  },\n"
393         + "\n"
394         + "  SCISSORS(\"peace sign\");\n"
395         + "\n"
396         + "  private final String handPosition;\n"
397         + "\n"
398         + "  Roshambo(String handPosition) {\n"
399         + "    this.handPosition = handPosition;\n"
400         + "  }\n"
401         + "\n"
402         + "  Roshambo() {\n"
403         + "    this(\"fist\");\n"
404         + "  }\n"
405         + "}\n");
406   }
407 
408   /** https://github.com/square/javapoet/issues/193 */
enumsMayDefineAbstractMethods()409   @Test public void enumsMayDefineAbstractMethods() throws Exception {
410     TypeSpec roshambo = TypeSpec.enumBuilder("Tortilla")
411         .addModifiers(Modifier.PUBLIC)
412         .addEnumConstant("CORN", TypeSpec.anonymousClassBuilder("")
413             .addMethod(MethodSpec.methodBuilder("fold")
414                 .addAnnotation(Override.class)
415                 .addModifiers(Modifier.PUBLIC)
416                 .build())
417             .build())
418         .addMethod(MethodSpec.methodBuilder("fold")
419             .addModifiers(Modifier.PUBLIC, Modifier.ABSTRACT)
420             .build())
421         .build();
422     assertThat(toString(roshambo)).isEqualTo(""
423         + "package com.squareup.tacos;\n"
424         + "\n"
425         + "import java.lang.Override;\n"
426         + "\n"
427         + "public enum Tortilla {\n"
428         + "  CORN {\n"
429         + "    @Override\n"
430         + "    public void fold() {\n"
431         + "    }\n"
432         + "  };\n"
433         + "\n"
434         + "  public abstract void fold();\n"
435         + "}\n");
436   }
437 
enumConstantsRequired()438   @Test public void enumConstantsRequired() throws Exception {
439     try {
440       TypeSpec.enumBuilder("Roshambo")
441           .build();
442       fail();
443     } catch (IllegalArgumentException expected) {
444     }
445   }
446 
onlyEnumsMayHaveEnumConstants()447   @Test public void onlyEnumsMayHaveEnumConstants() throws Exception {
448     try {
449       TypeSpec.classBuilder("Roshambo")
450           .addEnumConstant("ROCK")
451           .build();
452       fail();
453     } catch (IllegalStateException expected) {
454     }
455   }
456 
enumWithMembersButNoConstructorCall()457   @Test public void enumWithMembersButNoConstructorCall() throws Exception {
458     TypeSpec roshambo = TypeSpec.enumBuilder("Roshambo")
459         .addEnumConstant("SPOCK", TypeSpec.anonymousClassBuilder("")
460             .addMethod(MethodSpec.methodBuilder("toString")
461                 .addAnnotation(Override.class)
462                 .addModifiers(Modifier.PUBLIC)
463                 .returns(String.class)
464                 .addCode("return $S;\n", "west side")
465                 .build())
466             .build())
467         .build();
468     assertThat(toString(roshambo)).isEqualTo(""
469         + "package com.squareup.tacos;\n"
470         + "\n"
471         + "import java.lang.Override;\n"
472         + "import java.lang.String;\n"
473         + "\n"
474         + "enum Roshambo {\n"
475         + "  SPOCK {\n"
476         + "    @Override\n"
477         + "    public String toString() {\n"
478         + "      return \"west side\";\n"
479         + "    }\n"
480         + "  }\n"
481         + "}\n");
482   }
483 
484   /** https://github.com/square/javapoet/issues/253 */
enumWithAnnotatedValues()485   @Test public void enumWithAnnotatedValues() throws Exception {
486     TypeSpec roshambo = TypeSpec.enumBuilder("Roshambo")
487         .addModifiers(Modifier.PUBLIC)
488         .addEnumConstant("ROCK", TypeSpec.anonymousClassBuilder("")
489             .addAnnotation(Deprecated.class)
490             .build())
491         .addEnumConstant("PAPER")
492         .addEnumConstant("SCISSORS")
493         .build();
494     assertThat(toString(roshambo)).isEqualTo(""
495         + "package com.squareup.tacos;\n"
496         + "\n"
497         + "import java.lang.Deprecated;\n"
498         + "\n"
499         + "public enum Roshambo {\n"
500         + "  @Deprecated\n"
501         + "  ROCK,\n"
502         + "\n"
503         + "  PAPER,\n"
504         + "\n"
505         + "  SCISSORS\n"
506         + "}\n");
507   }
508 
methodThrows()509   @Test public void methodThrows() throws Exception {
510     TypeSpec taco = TypeSpec.classBuilder("Taco")
511         .addModifiers(Modifier.ABSTRACT)
512         .addMethod(MethodSpec.methodBuilder("throwOne")
513             .addException(IOException.class)
514             .build())
515         .addMethod(MethodSpec.methodBuilder("throwTwo")
516             .addException(IOException.class)
517             .addException(ClassName.get(tacosPackage, "SourCreamException"))
518             .build())
519         .addMethod(MethodSpec.methodBuilder("abstractThrow")
520             .addModifiers(Modifier.ABSTRACT)
521             .addException(IOException.class)
522             .build())
523         .addMethod(MethodSpec.methodBuilder("nativeThrow")
524             .addModifiers(Modifier.NATIVE)
525             .addException(IOException.class)
526             .build())
527         .build();
528     assertThat(toString(taco)).isEqualTo(""
529         + "package com.squareup.tacos;\n"
530         + "\n"
531         + "import java.io.IOException;\n"
532         + "\n"
533         + "abstract class Taco {\n"
534         + "  void throwOne() throws IOException {\n"
535         + "  }\n"
536         + "\n"
537         + "  void throwTwo() throws IOException, SourCreamException {\n"
538         + "  }\n"
539         + "\n"
540         + "  abstract void abstractThrow() throws IOException;\n"
541         + "\n"
542         + "  native void nativeThrow() throws IOException;\n"
543         + "}\n");
544   }
545 
typeVariables()546   @Test public void typeVariables() throws Exception {
547     TypeVariableName t = TypeVariableName.get("T");
548     TypeVariableName p = TypeVariableName.get("P", Number.class);
549     ClassName location = ClassName.get(tacosPackage, "Location");
550     TypeSpec typeSpec = TypeSpec.classBuilder("Location")
551         .addTypeVariable(t)
552         .addTypeVariable(p)
553         .addSuperinterface(ParameterizedTypeName.get(ClassName.get(Comparable.class), p))
554         .addField(t, "label")
555         .addField(p, "x")
556         .addField(p, "y")
557         .addMethod(MethodSpec.methodBuilder("compareTo")
558             .addAnnotation(Override.class)
559             .addModifiers(Modifier.PUBLIC)
560             .returns(int.class)
561             .addParameter(p, "p")
562             .addCode("return 0;\n")
563             .build())
564         .addMethod(MethodSpec.methodBuilder("of")
565             .addModifiers(Modifier.PUBLIC, Modifier.STATIC)
566             .addTypeVariable(t)
567             .addTypeVariable(p)
568             .returns(ParameterizedTypeName.get(location, t, p))
569             .addParameter(t, "label")
570             .addParameter(p, "x")
571             .addParameter(p, "y")
572             .addCode("throw new $T($S);\n", UnsupportedOperationException.class, "TODO")
573             .build())
574         .build();
575     assertThat(toString(typeSpec)).isEqualTo(""
576         + "package com.squareup.tacos;\n"
577         + "\n"
578         + "import java.lang.Comparable;\n"
579         + "import java.lang.Number;\n"
580         + "import java.lang.Override;\n"
581         + "import java.lang.UnsupportedOperationException;\n"
582         + "\n"
583         + "class Location<T, P extends Number> implements Comparable<P> {\n"
584         + "  T label;\n"
585         + "\n"
586         + "  P x;\n"
587         + "\n"
588         + "  P y;\n"
589         + "\n"
590         + "  @Override\n"
591         + "  public int compareTo(P p) {\n"
592         + "    return 0;\n"
593         + "  }\n"
594         + "\n"
595         + "  public static <T, P extends Number> Location<T, P> of(T label, P x, P y) {\n"
596         + "    throw new UnsupportedOperationException(\"TODO\");\n"
597         + "  }\n"
598         + "}\n");
599   }
600 
typeVariableWithBounds()601   @Test public void typeVariableWithBounds() {
602     AnnotationSpec a = AnnotationSpec.builder(ClassName.get("com.squareup.tacos", "A")).build();
603     TypeVariableName p = TypeVariableName.get("P", Number.class);
604     TypeVariableName q = (TypeVariableName) TypeVariableName.get("Q", Number.class).annotated(a);
605     TypeSpec typeSpec = TypeSpec.classBuilder("Location")
606         .addTypeVariable(p.withBounds(Comparable.class))
607         .addTypeVariable(q.withBounds(Comparable.class))
608         .addField(p, "x")
609         .addField(q, "y")
610         .build();
611     assertThat(toString(typeSpec)).isEqualTo(""
612         + "package com.squareup.tacos;\n"
613         + "\n"
614         + "import java.lang.Comparable;\n"
615         + "import java.lang.Number;\n"
616         + "\n"
617         + "class Location<P extends Number & Comparable, @A Q extends Number & Comparable> {\n"
618         + "  P x;\n"
619         + "\n"
620         + "  @A Q y;\n"
621         + "}\n");
622   }
623 
classImplementsExtends()624   @Test public void classImplementsExtends() throws Exception {
625     ClassName taco = ClassName.get(tacosPackage, "Taco");
626     ClassName food = ClassName.get("com.squareup.tacos", "Food");
627     TypeSpec typeSpec = TypeSpec.classBuilder("Taco")
628         .addModifiers(Modifier.ABSTRACT)
629         .superclass(ParameterizedTypeName.get(ClassName.get(AbstractSet.class), food))
630         .addSuperinterface(Serializable.class)
631         .addSuperinterface(ParameterizedTypeName.get(ClassName.get(Comparable.class), taco))
632         .build();
633     assertThat(toString(typeSpec)).isEqualTo(""
634         + "package com.squareup.tacos;\n"
635         + "\n"
636         + "import java.io.Serializable;\n"
637         + "import java.lang.Comparable;\n"
638         + "import java.util.AbstractSet;\n"
639         + "\n"
640         + "abstract class Taco extends AbstractSet<Food> "
641         + "implements Serializable, Comparable<Taco> {\n"
642         + "}\n");
643   }
644 
classImplementsNestedClass()645   @Test public void classImplementsNestedClass() throws Exception {
646     ClassName outer = ClassName.get(tacosPackage, "Outer");
647     ClassName inner = outer.nestedClass("Inner");
648     ClassName callable = ClassName.get(Callable.class);
649     TypeSpec typeSpec = TypeSpec.classBuilder("Outer")
650         .superclass(ParameterizedTypeName.get(callable,
651             inner))
652         .addType(TypeSpec.classBuilder("Inner")
653             .addModifiers(Modifier.STATIC)
654             .build())
655         .build();
656 
657     assertThat(toString(typeSpec)).isEqualTo(""
658         + "package com.squareup.tacos;\n"
659         + "\n"
660         + "import java.util.concurrent.Callable;\n"
661         + "\n"
662         + "class Outer extends Callable<Outer.Inner> {\n"
663         + "  static class Inner {\n"
664         + "  }\n"
665         + "}\n");
666   }
667 
enumImplements()668   @Test public void enumImplements() throws Exception {
669     TypeSpec typeSpec = TypeSpec.enumBuilder("Food")
670         .addSuperinterface(Serializable.class)
671         .addSuperinterface(Cloneable.class)
672         .addEnumConstant("LEAN_GROUND_BEEF")
673         .addEnumConstant("SHREDDED_CHEESE")
674         .build();
675     assertThat(toString(typeSpec)).isEqualTo(""
676         + "package com.squareup.tacos;\n"
677         + "\n"
678         + "import java.io.Serializable;\n"
679         + "import java.lang.Cloneable;\n"
680         + "\n"
681         + "enum Food implements Serializable, Cloneable {\n"
682         + "  LEAN_GROUND_BEEF,\n"
683         + "\n"
684         + "  SHREDDED_CHEESE\n"
685         + "}\n");
686   }
687 
interfaceExtends()688   @Test public void interfaceExtends() throws Exception {
689     ClassName taco = ClassName.get(tacosPackage, "Taco");
690     TypeSpec typeSpec = TypeSpec.interfaceBuilder("Taco")
691         .addSuperinterface(Serializable.class)
692         .addSuperinterface(ParameterizedTypeName.get(ClassName.get(Comparable.class), taco))
693         .build();
694     assertThat(toString(typeSpec)).isEqualTo(""
695         + "package com.squareup.tacos;\n"
696         + "\n"
697         + "import java.io.Serializable;\n"
698         + "import java.lang.Comparable;\n"
699         + "\n"
700         + "interface Taco extends Serializable, Comparable<Taco> {\n"
701         + "}\n");
702   }
703 
nestedClasses()704   @Test public void nestedClasses() throws Exception {
705     ClassName taco = ClassName.get(tacosPackage, "Combo", "Taco");
706     ClassName topping = ClassName.get(tacosPackage, "Combo", "Taco", "Topping");
707     ClassName chips = ClassName.get(tacosPackage, "Combo", "Chips");
708     ClassName sauce = ClassName.get(tacosPackage, "Combo", "Sauce");
709     TypeSpec typeSpec = TypeSpec.classBuilder("Combo")
710         .addField(taco, "taco")
711         .addField(chips, "chips")
712         .addType(TypeSpec.classBuilder(taco.simpleName())
713             .addModifiers(Modifier.STATIC)
714             .addField(ParameterizedTypeName.get(ClassName.get(List.class), topping), "toppings")
715             .addField(sauce, "sauce")
716             .addType(TypeSpec.enumBuilder(topping.simpleName())
717                 .addEnumConstant("SHREDDED_CHEESE")
718                 .addEnumConstant("LEAN_GROUND_BEEF")
719                 .build())
720             .build())
721         .addType(TypeSpec.classBuilder(chips.simpleName())
722             .addModifiers(Modifier.STATIC)
723             .addField(topping, "topping")
724             .addField(sauce, "dippingSauce")
725             .build())
726         .addType(TypeSpec.enumBuilder(sauce.simpleName())
727             .addEnumConstant("SOUR_CREAM")
728             .addEnumConstant("SALSA")
729             .addEnumConstant("QUESO")
730             .addEnumConstant("MILD")
731             .addEnumConstant("FIRE")
732             .build())
733         .build();
734 
735     assertThat(toString(typeSpec)).isEqualTo(""
736         + "package com.squareup.tacos;\n"
737         + "\n"
738         + "import java.util.List;\n"
739         + "\n"
740         + "class Combo {\n"
741         + "  Taco taco;\n"
742         + "\n"
743         + "  Chips chips;\n"
744         + "\n"
745         + "  static class Taco {\n"
746         + "    List<Topping> toppings;\n"
747         + "\n"
748         + "    Sauce sauce;\n"
749         + "\n"
750         + "    enum Topping {\n"
751         + "      SHREDDED_CHEESE,\n"
752         + "\n"
753         + "      LEAN_GROUND_BEEF\n"
754         + "    }\n"
755         + "  }\n"
756         + "\n"
757         + "  static class Chips {\n"
758         + "    Taco.Topping topping;\n"
759         + "\n"
760         + "    Sauce dippingSauce;\n"
761         + "  }\n"
762         + "\n"
763         + "  enum Sauce {\n"
764         + "    SOUR_CREAM,\n"
765         + "\n"
766         + "    SALSA,\n"
767         + "\n"
768         + "    QUESO,\n"
769         + "\n"
770         + "    MILD,\n"
771         + "\n"
772         + "    FIRE\n"
773         + "  }\n"
774         + "}\n");
775   }
776 
annotation()777   @Test public void annotation() throws Exception {
778     TypeSpec annotation = TypeSpec.annotationBuilder("MyAnnotation")
779         .addModifiers(Modifier.PUBLIC)
780         .addMethod(MethodSpec.methodBuilder("test")
781             .addModifiers(Modifier.PUBLIC, Modifier.ABSTRACT)
782             .defaultValue("$L", 0)
783             .returns(int.class)
784             .build())
785         .build();
786 
787     assertThat(toString(annotation)).isEqualTo(""
788         + "package com.squareup.tacos;\n"
789         + "\n"
790         + "public @interface MyAnnotation {\n"
791         + "  int test() default 0;\n"
792         + "}\n"
793     );
794   }
795 
innerAnnotationInAnnotationDeclaration()796   @Test public void innerAnnotationInAnnotationDeclaration() throws Exception {
797     TypeSpec bar = TypeSpec.annotationBuilder("Bar")
798         .addMethod(MethodSpec.methodBuilder("value")
799             .addModifiers(Modifier.PUBLIC, Modifier.ABSTRACT)
800             .defaultValue("@$T", Deprecated.class)
801             .returns(Deprecated.class)
802             .build())
803         .build();
804 
805     assertThat(toString(bar)).isEqualTo(""
806         + "package com.squareup.tacos;\n"
807         + "\n"
808         + "import java.lang.Deprecated;\n"
809         + "\n"
810         + "@interface Bar {\n"
811         + "  Deprecated value() default @Deprecated;\n"
812         + "}\n"
813     );
814   }
815 
annotationWithFields()816   @Test public void annotationWithFields() {
817     FieldSpec field = FieldSpec.builder(int.class, "FOO")
818         .addModifiers(Modifier.PUBLIC, Modifier.STATIC, Modifier.FINAL)
819         .initializer("$L", 101)
820         .build();
821 
822     TypeSpec anno = TypeSpec.annotationBuilder("Anno")
823         .addField(field)
824         .build();
825 
826     assertThat(toString(anno)).isEqualTo(""
827         + "package com.squareup.tacos;\n"
828         + "\n"
829         + "@interface Anno {\n"
830         + "  int FOO = 101;\n"
831         + "}\n"
832     );
833   }
834 
835   @Test
classCannotHaveDefaultValueForMethod()836   public void classCannotHaveDefaultValueForMethod() throws Exception {
837     try {
838       TypeSpec.classBuilder("Tacos")
839           .addMethod(MethodSpec.methodBuilder("test")
840               .addModifiers(Modifier.PUBLIC)
841               .defaultValue("0")
842               .returns(int.class)
843               .build())
844           .build();
845       fail();
846     } catch (IllegalStateException expected) {
847     }
848   }
849 
850   @Test
classCannotHaveDefaultMethods()851   public void classCannotHaveDefaultMethods() throws Exception {
852     try {
853       TypeSpec.classBuilder("Tacos")
854           .addMethod(MethodSpec.methodBuilder("test")
855               .addModifiers(Modifier.PUBLIC, Modifier.DEFAULT)
856               .returns(int.class)
857               .addCode(CodeBlock.builder().addStatement("return 0").build())
858               .build())
859           .build();
860       fail();
861     } catch (IllegalStateException expected) {
862     }
863   }
864 
865   @Test
interfaceStaticMethods()866   public void interfaceStaticMethods() throws Exception {
867     TypeSpec bar = TypeSpec.interfaceBuilder("Tacos")
868         .addMethod(MethodSpec.methodBuilder("test")
869             .addModifiers(Modifier.PUBLIC, Modifier.STATIC)
870             .returns(int.class)
871             .addCode(CodeBlock.builder().addStatement("return 0").build())
872             .build())
873         .build();
874 
875     assertThat(toString(bar)).isEqualTo(""
876         + "package com.squareup.tacos;\n"
877         + "\n"
878         + "interface Tacos {\n"
879         + "  static int test() {\n"
880         + "    return 0;\n"
881         + "  }\n"
882         + "}\n"
883     );
884   }
885 
886   @Test
interfaceDefaultMethods()887   public void interfaceDefaultMethods() throws Exception {
888     TypeSpec bar = TypeSpec.interfaceBuilder("Tacos")
889         .addMethod(MethodSpec.methodBuilder("test")
890             .addModifiers(Modifier.PUBLIC, Modifier.DEFAULT)
891             .returns(int.class)
892             .addCode(CodeBlock.builder().addStatement("return 0").build())
893             .build())
894         .build();
895 
896     assertThat(toString(bar)).isEqualTo(""
897         + "package com.squareup.tacos;\n"
898         + "\n"
899         + "interface Tacos {\n"
900         + "  default int test() {\n"
901         + "    return 0;\n"
902         + "  }\n"
903         + "}\n"
904     );
905   }
906 
referencedAndDeclaredSimpleNamesConflict()907   @Test public void referencedAndDeclaredSimpleNamesConflict() throws Exception {
908     FieldSpec internalTop = FieldSpec.builder(
909         ClassName.get(tacosPackage, "Top"), "internalTop").build();
910     FieldSpec internalBottom = FieldSpec.builder(
911         ClassName.get(tacosPackage, "Top", "Middle", "Bottom"), "internalBottom").build();
912     FieldSpec externalTop = FieldSpec.builder(
913         ClassName.get(donutsPackage, "Top"), "externalTop").build();
914     FieldSpec externalBottom = FieldSpec.builder(
915         ClassName.get(donutsPackage, "Bottom"), "externalBottom").build();
916     TypeSpec top = TypeSpec.classBuilder("Top")
917         .addField(internalTop)
918         .addField(internalBottom)
919         .addField(externalTop)
920         .addField(externalBottom)
921         .addType(TypeSpec.classBuilder("Middle")
922             .addField(internalTop)
923             .addField(internalBottom)
924             .addField(externalTop)
925             .addField(externalBottom)
926             .addType(TypeSpec.classBuilder("Bottom")
927                 .addField(internalTop)
928                 .addField(internalBottom)
929                 .addField(externalTop)
930                 .addField(externalBottom)
931                 .build())
932             .build())
933         .build();
934     assertThat(toString(top)).isEqualTo(""
935         + "package com.squareup.tacos;\n"
936         + "\n"
937         + "import com.squareup.donuts.Bottom;\n"
938         + "\n"
939         + "class Top {\n"
940         + "  Top internalTop;\n"
941         + "\n"
942         + "  Middle.Bottom internalBottom;\n"
943         + "\n"
944         + "  com.squareup.donuts.Top externalTop;\n"
945         + "\n"
946         + "  Bottom externalBottom;\n"
947         + "\n"
948         + "  class Middle {\n"
949         + "    Top internalTop;\n"
950         + "\n"
951         + "    Bottom internalBottom;\n"
952         + "\n"
953         + "    com.squareup.donuts.Top externalTop;\n"
954         + "\n"
955         + "    com.squareup.donuts.Bottom externalBottom;\n"
956         + "\n"
957         + "    class Bottom {\n"
958         + "      Top internalTop;\n"
959         + "\n"
960         + "      Bottom internalBottom;\n"
961         + "\n"
962         + "      com.squareup.donuts.Top externalTop;\n"
963         + "\n"
964         + "      com.squareup.donuts.Bottom externalBottom;\n"
965         + "    }\n"
966         + "  }\n"
967         + "}\n");
968   }
969 
simpleNamesConflictInThisAndOtherPackage()970   @Test public void simpleNamesConflictInThisAndOtherPackage() throws Exception {
971     FieldSpec internalOther = FieldSpec.builder(
972         ClassName.get(tacosPackage, "Other"), "internalOther").build();
973     FieldSpec externalOther = FieldSpec.builder(
974         ClassName.get(donutsPackage, "Other"), "externalOther").build();
975     TypeSpec gen = TypeSpec.classBuilder("Gen")
976         .addField(internalOther)
977         .addField(externalOther)
978         .build();
979     assertThat(toString(gen)).isEqualTo(""
980         + "package com.squareup.tacos;\n"
981         + "\n"
982         + "class Gen {\n"
983         + "  Other internalOther;\n"
984         + "\n"
985         + "  com.squareup.donuts.Other externalOther;\n"
986         + "}\n");
987   }
988 
simpleNameConflictsWithTypeVariable()989   @Test public void simpleNameConflictsWithTypeVariable() {
990     ClassName inPackage = ClassName.get("com.squareup.tacos", "InPackage");
991     ClassName otherType = ClassName.get("com.other", "OtherType");
992     ClassName methodInPackage = ClassName.get("com.squareup.tacos", "MethodInPackage");
993     ClassName methodOtherType = ClassName.get("com.other", "MethodOtherType");
994     TypeSpec gen = TypeSpec.classBuilder("Gen")
995         .addTypeVariable(TypeVariableName.get("InPackage"))
996         .addTypeVariable(TypeVariableName.get("OtherType"))
997         .addField(FieldSpec.builder(inPackage, "inPackage").build())
998         .addField(FieldSpec.builder(otherType, "otherType").build())
999         .addMethod(MethodSpec.methodBuilder("withTypeVariables")
1000             .addTypeVariable(TypeVariableName.get("MethodInPackage"))
1001             .addTypeVariable(TypeVariableName.get("MethodOtherType"))
1002             .addStatement("$T inPackage = null", methodInPackage)
1003             .addStatement("$T otherType = null", methodOtherType)
1004             .build())
1005         .addMethod(MethodSpec.methodBuilder("withoutTypeVariables")
1006             .addStatement("$T inPackage = null", methodInPackage)
1007             .addStatement("$T otherType = null", methodOtherType)
1008             .build())
1009         .addMethod(MethodSpec.methodBuilder("againWithTypeVariables")
1010             .addTypeVariable(TypeVariableName.get("MethodInPackage"))
1011             .addTypeVariable(TypeVariableName.get("MethodOtherType"))
1012             .addStatement("$T inPackage = null", methodInPackage)
1013             .addStatement("$T otherType = null", methodOtherType)
1014             .build())
1015         // https://github.com/square/javapoet/pull/657#discussion_r205514292
1016         .addMethod(MethodSpec.methodBuilder("masksEnclosingTypeVariable")
1017             .addTypeVariable(TypeVariableName.get("InPackage"))
1018             .build())
1019         .addMethod(MethodSpec.methodBuilder("hasSimpleNameThatWasPreviouslyMasked")
1020             .addStatement("$T inPackage = null", inPackage)
1021             .build())
1022         .build();
1023     assertThat(toString(gen)).isEqualTo(""
1024         + "package com.squareup.tacos;\n"
1025         + "\n"
1026         + "import com.other.MethodOtherType;\n"
1027         + "\n"
1028         + "class Gen<InPackage, OtherType> {\n"
1029         + "  com.squareup.tacos.InPackage inPackage;\n"
1030         + "\n"
1031         + "  com.other.OtherType otherType;\n"
1032         + "\n"
1033         + "  <MethodInPackage, MethodOtherType> void withTypeVariables() {\n"
1034         + "    com.squareup.tacos.MethodInPackage inPackage = null;\n"
1035         + "    com.other.MethodOtherType otherType = null;\n"
1036         + "  }\n"
1037         + "\n"
1038         + "  void withoutTypeVariables() {\n"
1039         + "    MethodInPackage inPackage = null;\n"
1040         + "    MethodOtherType otherType = null;\n"
1041         + "  }\n"
1042         + "\n"
1043         + "  <MethodInPackage, MethodOtherType> void againWithTypeVariables() {\n"
1044         + "    com.squareup.tacos.MethodInPackage inPackage = null;\n"
1045         + "    com.other.MethodOtherType otherType = null;\n"
1046         + "  }\n"
1047         + "\n"
1048         + "  <InPackage> void masksEnclosingTypeVariable() {\n"
1049         + "  }\n"
1050         + "\n"
1051         + "  void hasSimpleNameThatWasPreviouslyMasked() {\n"
1052         + "    com.squareup.tacos.InPackage inPackage = null;\n"
1053         + "  }\n"
1054         + "}\n");
1055   }
1056 
originatingElementsIncludesThoseOfNestedTypes()1057   @Test public void originatingElementsIncludesThoseOfNestedTypes() {
1058     Element outerElement = Mockito.mock(Element.class);
1059     Element innerElement = Mockito.mock(Element.class);
1060     TypeSpec outer = TypeSpec.classBuilder("Outer")
1061         .addOriginatingElement(outerElement)
1062         .addType(TypeSpec.classBuilder("Inner")
1063             .addOriginatingElement(innerElement)
1064             .build())
1065         .build();
1066     assertThat(outer.originatingElements).containsExactly(outerElement, innerElement);
1067   }
1068 
intersectionType()1069   @Test public void intersectionType() {
1070     TypeVariableName typeVariable = TypeVariableName.get("T", Comparator.class, Serializable.class);
1071     TypeSpec taco = TypeSpec.classBuilder("Taco")
1072         .addMethod(MethodSpec.methodBuilder("getComparator")
1073             .addTypeVariable(typeVariable)
1074             .returns(typeVariable)
1075             .addCode("return null;\n")
1076             .build())
1077         .build();
1078     assertThat(toString(taco)).isEqualTo(""
1079         + "package com.squareup.tacos;\n"
1080         + "\n"
1081         + "import java.io.Serializable;\n"
1082         + "import java.util.Comparator;\n"
1083         + "\n"
1084         + "class Taco {\n"
1085         + "  <T extends Comparator & Serializable> T getComparator() {\n"
1086         + "    return null;\n"
1087         + "  }\n"
1088         + "}\n");
1089   }
1090 
arrayType()1091   @Test public void arrayType() {
1092     TypeSpec taco = TypeSpec.classBuilder("Taco")
1093         .addField(int[].class, "ints")
1094         .build();
1095     assertThat(toString(taco)).isEqualTo(""
1096         + "package com.squareup.tacos;\n"
1097         + "\n"
1098         + "class Taco {\n"
1099         + "  int[] ints;\n"
1100         + "}\n");
1101   }
1102 
javadoc()1103   @Test public void javadoc() {
1104     TypeSpec taco = TypeSpec.classBuilder("Taco")
1105         .addJavadoc("A hard or soft tortilla, loosely folded and filled with whatever {@link \n")
1106         .addJavadoc("{@link $T random} tex-mex stuff we could find in the pantry\n", Random.class)
1107         .addJavadoc(CodeBlock.of("and some {@link $T} cheese.\n", String.class))
1108         .addField(FieldSpec.builder(boolean.class, "soft")
1109             .addJavadoc("True for a soft flour tortilla; false for a crunchy corn tortilla.\n")
1110             .build())
1111         .addMethod(MethodSpec.methodBuilder("refold")
1112             .addJavadoc("Folds the back of this taco to reduce sauce leakage.\n"
1113                 + "\n"
1114                 + "<p>For {@link $T#KOREAN}, the front may also be folded.\n", Locale.class)
1115             .addParameter(Locale.class, "locale")
1116             .build())
1117         .build();
1118     // Mentioning a type in Javadoc will not cause an import to be added (java.util.Random here),
1119     // but the short name will be used if it's already imported (java.util.Locale here).
1120     assertThat(toString(taco)).isEqualTo(""
1121         + "package com.squareup.tacos;\n"
1122         + "\n"
1123         + "import java.util.Locale;\n"
1124         + "\n"
1125         + "/**\n"
1126         + " * A hard or soft tortilla, loosely folded and filled with whatever {@link \n"
1127         + " * {@link java.util.Random random} tex-mex stuff we could find in the pantry\n"
1128         + " * and some {@link java.lang.String} cheese.\n"
1129         + " */\n"
1130         + "class Taco {\n"
1131         + "  /**\n"
1132         + "   * True for a soft flour tortilla; false for a crunchy corn tortilla.\n"
1133         + "   */\n"
1134         + "  boolean soft;\n"
1135         + "\n"
1136         + "  /**\n"
1137         + "   * Folds the back of this taco to reduce sauce leakage.\n"
1138         + "   *\n"
1139         + "   * <p>For {@link Locale#KOREAN}, the front may also be folded.\n"
1140         + "   */\n"
1141         + "  void refold(Locale locale) {\n"
1142         + "  }\n"
1143         + "}\n");
1144   }
1145 
annotationsInAnnotations()1146   @Test public void annotationsInAnnotations() throws Exception {
1147     ClassName beef = ClassName.get(tacosPackage, "Beef");
1148     ClassName chicken = ClassName.get(tacosPackage, "Chicken");
1149     ClassName option = ClassName.get(tacosPackage, "Option");
1150     ClassName mealDeal = ClassName.get(tacosPackage, "MealDeal");
1151     TypeSpec menu = TypeSpec.classBuilder("Menu")
1152         .addAnnotation(AnnotationSpec.builder(mealDeal)
1153             .addMember("price", "$L", 500)
1154             .addMember("options", "$L", AnnotationSpec.builder(option)
1155                 .addMember("name", "$S", "taco")
1156                 .addMember("meat", "$T.class", beef)
1157                 .build())
1158             .addMember("options", "$L", AnnotationSpec.builder(option)
1159                 .addMember("name", "$S", "quesadilla")
1160                 .addMember("meat", "$T.class", chicken)
1161                 .build())
1162             .build())
1163         .build();
1164     assertThat(toString(menu)).isEqualTo(""
1165         + "package com.squareup.tacos;\n"
1166         + "\n"
1167         + "@MealDeal(\n"
1168         + "    price = 500,\n"
1169         + "    options = {\n"
1170         + "        @Option(name = \"taco\", meat = Beef.class),\n"
1171         + "        @Option(name = \"quesadilla\", meat = Chicken.class)\n"
1172         + "    }\n"
1173         + ")\n"
1174         + "class Menu {\n"
1175         + "}\n");
1176   }
1177 
varargs()1178   @Test public void varargs() throws Exception {
1179     TypeSpec taqueria = TypeSpec.classBuilder("Taqueria")
1180         .addMethod(MethodSpec.methodBuilder("prepare")
1181             .addParameter(int.class, "workers")
1182             .addParameter(Runnable[].class, "jobs")
1183             .varargs()
1184             .build())
1185         .build();
1186     assertThat(toString(taqueria)).isEqualTo(""
1187         + "package com.squareup.tacos;\n"
1188         + "\n"
1189         + "import java.lang.Runnable;\n"
1190         + "\n"
1191         + "class Taqueria {\n"
1192         + "  void prepare(int workers, Runnable... jobs) {\n"
1193         + "  }\n"
1194         + "}\n");
1195   }
1196 
codeBlocks()1197   @Test public void codeBlocks() throws Exception {
1198     CodeBlock ifBlock = CodeBlock.builder()
1199         .beginControlFlow("if (!a.equals(b))")
1200         .addStatement("return i")
1201         .endControlFlow()
1202         .build();
1203     CodeBlock methodBody = CodeBlock.builder()
1204         .addStatement("$T size = $T.min(listA.size(), listB.size())", int.class, Math.class)
1205         .beginControlFlow("for ($T i = 0; i < size; i++)", int.class)
1206         .addStatement("$T $N = $N.get(i)", String.class, "a", "listA")
1207         .addStatement("$T $N = $N.get(i)", String.class, "b", "listB")
1208         .add("$L", ifBlock)
1209         .endControlFlow()
1210         .addStatement("return size")
1211         .build();
1212     CodeBlock fieldBlock = CodeBlock.builder()
1213         .add("$>$>")
1214         .add("\n$T.<$T, $T>builder()$>$>", ImmutableMap.class, String.class, String.class)
1215         .add("\n.add($S, $S)", '\'', "&#39;")
1216         .add("\n.add($S, $S)", '&', "&amp;")
1217         .add("\n.add($S, $S)", '<', "&lt;")
1218         .add("\n.add($S, $S)", '>', "&gt;")
1219         .add("\n.build()$<$<")
1220         .add("$<$<")
1221         .build();
1222     FieldSpec escapeHtml = FieldSpec.builder(ParameterizedTypeName.get(
1223         Map.class, String.class, String.class), "ESCAPE_HTML")
1224         .addModifiers(Modifier.PRIVATE, Modifier.STATIC, Modifier.FINAL)
1225         .initializer(fieldBlock)
1226         .build();
1227     TypeSpec util = TypeSpec.classBuilder("Util")
1228         .addField(escapeHtml)
1229         .addMethod(MethodSpec.methodBuilder("commonPrefixLength")
1230             .returns(int.class)
1231             .addParameter(ParameterizedTypeName.get(List.class, String.class), "listA")
1232             .addParameter(ParameterizedTypeName.get(List.class, String.class), "listB")
1233             .addCode(methodBody)
1234             .build())
1235         .build();
1236     assertThat(toString(util)).isEqualTo(""
1237         + "package com.squareup.tacos;\n"
1238         + "\n"
1239         + "import com.google.common.collect.ImmutableMap;\n"
1240         + "import java.lang.Math;\n"
1241         + "import java.lang.String;\n"
1242         + "import java.util.List;\n"
1243         + "import java.util.Map;\n"
1244         + "\n"
1245         + "class Util {\n"
1246         + "  private static final Map<String, String> ESCAPE_HTML = \n"
1247         + "      ImmutableMap.<String, String>builder()\n"
1248         + "          .add(\"\'\", \"&#39;\")\n"
1249         + "          .add(\"&\", \"&amp;\")\n"
1250         + "          .add(\"<\", \"&lt;\")\n"
1251         + "          .add(\">\", \"&gt;\")\n"
1252         + "          .build();\n"
1253         + "\n"
1254         + "  int commonPrefixLength(List<String> listA, List<String> listB) {\n"
1255         + "    int size = Math.min(listA.size(), listB.size());\n"
1256         + "    for (int i = 0; i < size; i++) {\n"
1257         + "      String a = listA.get(i);\n"
1258         + "      String b = listB.get(i);\n"
1259         + "      if (!a.equals(b)) {\n"
1260         + "        return i;\n"
1261         + "      }\n"
1262         + "    }\n"
1263         + "    return size;\n"
1264         + "  }\n"
1265         + "}\n");
1266   }
1267 
indexedElseIf()1268   @Test public void indexedElseIf() throws Exception {
1269     TypeSpec taco = TypeSpec.classBuilder("Taco")
1270         .addMethod(MethodSpec.methodBuilder("choices")
1271             .beginControlFlow("if ($1L != null || $1L == $2L)", "taco", "otherTaco")
1272             .addStatement("$T.out.println($S)", System.class, "only one taco? NOO!")
1273             .nextControlFlow("else if ($1L.$3L && $2L.$3L)", "taco", "otherTaco", "isSupreme()")
1274             .addStatement("$T.out.println($S)", System.class, "taco heaven")
1275             .endControlFlow()
1276             .build())
1277         .build();
1278     assertThat(toString(taco)).isEqualTo(""
1279         + "package com.squareup.tacos;\n"
1280         + "\n"
1281         + "import java.lang.System;\n"
1282         + "\n"
1283         + "class Taco {\n"
1284         + "  void choices() {\n"
1285         + "    if (taco != null || taco == otherTaco) {\n"
1286         + "      System.out.println(\"only one taco? NOO!\");\n"
1287         + "    } else if (taco.isSupreme() && otherTaco.isSupreme()) {\n"
1288         + "      System.out.println(\"taco heaven\");\n"
1289         + "    }\n"
1290         + "  }\n"
1291         + "}\n");
1292   }
1293 
elseIf()1294   @Test public void elseIf() throws Exception {
1295     TypeSpec taco = TypeSpec.classBuilder("Taco")
1296         .addMethod(MethodSpec.methodBuilder("choices")
1297             .beginControlFlow("if (5 < 4) ")
1298             .addStatement("$T.out.println($S)", System.class, "wat")
1299             .nextControlFlow("else if (5 < 6)")
1300             .addStatement("$T.out.println($S)", System.class, "hello")
1301             .endControlFlow()
1302             .build())
1303         .build();
1304     assertThat(toString(taco)).isEqualTo(""
1305         + "package com.squareup.tacos;\n"
1306         + "\n"
1307         + "import java.lang.System;\n"
1308         + "\n"
1309         + "class Taco {\n"
1310         + "  void choices() {\n"
1311         + "    if (5 < 4)  {\n"
1312         + "      System.out.println(\"wat\");\n"
1313         + "    } else if (5 < 6) {\n"
1314         + "      System.out.println(\"hello\");\n"
1315         + "    }\n"
1316         + "  }\n"
1317         + "}\n");
1318   }
1319 
doWhile()1320   @Test public void doWhile() throws Exception {
1321     TypeSpec taco = TypeSpec.classBuilder("Taco")
1322         .addMethod(MethodSpec.methodBuilder("loopForever")
1323             .beginControlFlow("do")
1324             .addStatement("$T.out.println($S)", System.class, "hello")
1325             .endControlFlow("while (5 < 6)")
1326             .build())
1327         .build();
1328     assertThat(toString(taco)).isEqualTo(""
1329         + "package com.squareup.tacos;\n"
1330         + "\n"
1331         + "import java.lang.System;\n"
1332         + "\n"
1333         + "class Taco {\n"
1334         + "  void loopForever() {\n"
1335         + "    do {\n"
1336         + "      System.out.println(\"hello\");\n"
1337         + "    } while (5 < 6);\n"
1338         + "  }\n"
1339         + "}\n");
1340   }
1341 
inlineIndent()1342   @Test public void inlineIndent() throws Exception {
1343     TypeSpec taco = TypeSpec.classBuilder("Taco")
1344         .addMethod(MethodSpec.methodBuilder("inlineIndent")
1345             .addCode("if (3 < 4) {\n$>$T.out.println($S);\n$<}\n", System.class, "hello")
1346             .build())
1347         .build();
1348     assertThat(toString(taco)).isEqualTo(""
1349         + "package com.squareup.tacos;\n"
1350         + "\n"
1351         + "import java.lang.System;\n"
1352         + "\n"
1353         + "class Taco {\n"
1354         + "  void inlineIndent() {\n"
1355         + "    if (3 < 4) {\n"
1356         + "      System.out.println(\"hello\");\n"
1357         + "    }\n"
1358         + "  }\n"
1359         + "}\n");
1360   }
1361 
defaultModifiersForInterfaceMembers()1362   @Test public void defaultModifiersForInterfaceMembers() throws Exception {
1363     TypeSpec taco = TypeSpec.interfaceBuilder("Taco")
1364         .addField(FieldSpec.builder(String.class, "SHELL")
1365             .addModifiers(Modifier.PUBLIC, Modifier.STATIC, Modifier.FINAL)
1366             .initializer("$S", "crunchy corn")
1367             .build())
1368         .addMethod(MethodSpec.methodBuilder("fold")
1369             .addModifiers(Modifier.PUBLIC, Modifier.ABSTRACT)
1370             .build())
1371         .addType(TypeSpec.classBuilder("Topping")
1372             .addModifiers(Modifier.PUBLIC, Modifier.STATIC)
1373             .build())
1374         .build();
1375     assertThat(toString(taco)).isEqualTo(""
1376         + "package com.squareup.tacos;\n"
1377         + "\n"
1378         + "import java.lang.String;\n"
1379         + "\n"
1380         + "interface Taco {\n"
1381         + "  String SHELL = \"crunchy corn\";\n"
1382         + "\n"
1383         + "  void fold();\n"
1384         + "\n"
1385         + "  class Topping {\n"
1386         + "  }\n"
1387         + "}\n");
1388   }
1389 
defaultModifiersForMemberInterfacesAndEnums()1390   @Test public void defaultModifiersForMemberInterfacesAndEnums() throws Exception {
1391     TypeSpec taco = TypeSpec.classBuilder("Taco")
1392         .addType(TypeSpec.classBuilder("Meat")
1393             .addModifiers(Modifier.STATIC)
1394             .build())
1395         .addType(TypeSpec.interfaceBuilder("Tortilla")
1396             .addModifiers(Modifier.STATIC)
1397             .build())
1398         .addType(TypeSpec.enumBuilder("Topping")
1399             .addModifiers(Modifier.STATIC)
1400             .addEnumConstant("SALSA")
1401             .build())
1402         .build();
1403     assertThat(toString(taco)).isEqualTo(""
1404         + "package com.squareup.tacos;\n"
1405         + "\n"
1406         + "class Taco {\n"
1407         + "  static class Meat {\n"
1408         + "  }\n"
1409         + "\n"
1410         + "  interface Tortilla {\n"
1411         + "  }\n"
1412         + "\n"
1413         + "  enum Topping {\n"
1414         + "    SALSA\n"
1415         + "  }\n"
1416         + "}\n");
1417   }
1418 
membersOrdering()1419   @Test public void membersOrdering() throws Exception {
1420     // Hand out names in reverse-alphabetical order to defend against unexpected sorting.
1421     TypeSpec taco = TypeSpec.classBuilder("Members")
1422         .addType(TypeSpec.classBuilder("Z").build())
1423         .addType(TypeSpec.classBuilder("Y").build())
1424         .addField(String.class, "X", Modifier.STATIC)
1425         .addField(String.class, "W")
1426         .addField(String.class, "V", Modifier.STATIC)
1427         .addField(String.class, "U")
1428         .addMethod(MethodSpec.methodBuilder("T").addModifiers(Modifier.STATIC).build())
1429         .addMethod(MethodSpec.methodBuilder("S").build())
1430         .addMethod(MethodSpec.methodBuilder("R").addModifiers(Modifier.STATIC).build())
1431         .addMethod(MethodSpec.methodBuilder("Q").build())
1432         .addMethod(MethodSpec.constructorBuilder().addParameter(int.class, "p").build())
1433         .addMethod(MethodSpec.constructorBuilder().addParameter(long.class, "o").build())
1434         .build();
1435     // Static fields, instance fields, constructors, methods, classes.
1436     assertThat(toString(taco)).isEqualTo(""
1437         + "package com.squareup.tacos;\n"
1438         + "\n"
1439         + "import java.lang.String;\n"
1440         + "\n"
1441         + "class Members {\n"
1442         + "  static String X;\n"
1443         + "\n"
1444         + "  static String V;\n"
1445         + "\n"
1446         + "  String W;\n"
1447         + "\n"
1448         + "  String U;\n"
1449         + "\n"
1450         + "  Members(int p) {\n"
1451         + "  }\n"
1452         + "\n"
1453         + "  Members(long o) {\n"
1454         + "  }\n"
1455         + "\n"
1456         + "  static void T() {\n"
1457         + "  }\n"
1458         + "\n"
1459         + "  void S() {\n"
1460         + "  }\n"
1461         + "\n"
1462         + "  static void R() {\n"
1463         + "  }\n"
1464         + "\n"
1465         + "  void Q() {\n"
1466         + "  }\n"
1467         + "\n"
1468         + "  class Z {\n"
1469         + "  }\n"
1470         + "\n"
1471         + "  class Y {\n"
1472         + "  }\n"
1473         + "}\n");
1474   }
1475 
nativeMethods()1476   @Test public void nativeMethods() throws Exception {
1477     TypeSpec taco = TypeSpec.classBuilder("Taco")
1478         .addMethod(MethodSpec.methodBuilder("nativeInt")
1479             .addModifiers(Modifier.NATIVE)
1480             .returns(int.class)
1481             .build())
1482         // GWT JSNI
1483         .addMethod(MethodSpec.methodBuilder("alert")
1484             .addModifiers(Modifier.PUBLIC, Modifier.STATIC, Modifier.NATIVE)
1485             .addParameter(String.class, "msg")
1486             .addCode(CodeBlock.builder()
1487                 .add(" /*-{\n")
1488                 .indent()
1489                 .addStatement("$$wnd.alert(msg)")
1490                 .unindent()
1491                 .add("}-*/")
1492                 .build())
1493             .build())
1494         .build();
1495     assertThat(toString(taco)).isEqualTo(""
1496         + "package com.squareup.tacos;\n"
1497         + "\n"
1498         + "import java.lang.String;\n"
1499         + "\n"
1500         + "class Taco {\n"
1501         + "  native int nativeInt();\n"
1502         + "\n"
1503         + "  public static native void alert(String msg) /*-{\n"
1504         + "    $wnd.alert(msg);\n"
1505         + "  }-*/;\n"
1506         + "}\n");
1507   }
1508 
nullStringLiteral()1509   @Test public void nullStringLiteral() throws Exception {
1510     TypeSpec taco = TypeSpec.classBuilder("Taco")
1511         .addField(FieldSpec.builder(String.class, "NULL")
1512             .initializer("$S", (Object) null)
1513             .build())
1514         .build();
1515     assertThat(toString(taco)).isEqualTo(""
1516         + "package com.squareup.tacos;\n"
1517         + "\n"
1518         + "import java.lang.String;\n"
1519         + "\n"
1520         + "class Taco {\n"
1521         + "  String NULL = null;\n"
1522         + "}\n");
1523   }
1524 
annotationToString()1525   @Test public void annotationToString() throws Exception {
1526     AnnotationSpec annotation = AnnotationSpec.builder(SuppressWarnings.class)
1527         .addMember("value", "$S", "unused")
1528         .build();
1529     assertThat(annotation.toString()).isEqualTo("@java.lang.SuppressWarnings(\"unused\")");
1530   }
1531 
codeBlockToString()1532   @Test public void codeBlockToString() throws Exception {
1533     CodeBlock codeBlock = CodeBlock.builder()
1534         .addStatement("$T $N = $S.substring(0, 3)", String.class, "s", "taco")
1535         .build();
1536     assertThat(codeBlock.toString()).isEqualTo("java.lang.String s = \"taco\".substring(0, 3);\n");
1537   }
1538 
codeBlockAddStatementOfCodeBlockToString()1539   @Test public void codeBlockAddStatementOfCodeBlockToString() throws Exception {
1540     CodeBlock contents = CodeBlock.of("$T $N = $S.substring(0, 3)", String.class, "s", "taco");
1541     CodeBlock statement = CodeBlock.builder().addStatement(contents).build();
1542     assertThat(statement.toString()).isEqualTo("java.lang.String s = \"taco\".substring(0, 3);\n");
1543   }
1544 
fieldToString()1545   @Test public void fieldToString() throws Exception {
1546     FieldSpec field = FieldSpec.builder(String.class, "s", Modifier.FINAL)
1547         .initializer("$S.substring(0, 3)", "taco")
1548         .build();
1549     assertThat(field.toString())
1550         .isEqualTo("final java.lang.String s = \"taco\".substring(0, 3);\n");
1551   }
1552 
methodToString()1553   @Test public void methodToString() throws Exception {
1554     MethodSpec method = MethodSpec.methodBuilder("toString")
1555         .addAnnotation(Override.class)
1556         .addModifiers(Modifier.PUBLIC)
1557         .returns(String.class)
1558         .addStatement("return $S", "taco")
1559         .build();
1560     assertThat(method.toString()).isEqualTo(""
1561         + "@java.lang.Override\n"
1562         + "public java.lang.String toString() {\n"
1563         + "  return \"taco\";\n"
1564         + "}\n");
1565   }
1566 
constructorToString()1567   @Test public void constructorToString() throws Exception {
1568     MethodSpec constructor = MethodSpec.constructorBuilder()
1569         .addModifiers(Modifier.PUBLIC)
1570         .addParameter(ClassName.get(tacosPackage, "Taco"), "taco")
1571         .addStatement("this.$N = $N", "taco", "taco")
1572         .build();
1573     assertThat(constructor.toString()).isEqualTo(""
1574         + "public Constructor(com.squareup.tacos.Taco taco) {\n"
1575         + "  this.taco = taco;\n"
1576         + "}\n");
1577   }
1578 
parameterToString()1579   @Test public void parameterToString() throws Exception {
1580     ParameterSpec parameter = ParameterSpec.builder(ClassName.get(tacosPackage, "Taco"), "taco")
1581         .addModifiers(Modifier.FINAL)
1582         .addAnnotation(ClassName.get("javax.annotation", "Nullable"))
1583         .build();
1584     assertThat(parameter.toString())
1585         .isEqualTo("@javax.annotation.Nullable final com.squareup.tacos.Taco taco");
1586   }
1587 
classToString()1588   @Test public void classToString() throws Exception {
1589     TypeSpec type = TypeSpec.classBuilder("Taco")
1590         .build();
1591     assertThat(type.toString()).isEqualTo(""
1592         + "class Taco {\n"
1593         + "}\n");
1594   }
1595 
anonymousClassToString()1596   @Test public void anonymousClassToString() throws Exception {
1597     TypeSpec type = TypeSpec.anonymousClassBuilder("")
1598         .addSuperinterface(Runnable.class)
1599         .addMethod(MethodSpec.methodBuilder("run")
1600             .addAnnotation(Override.class)
1601             .addModifiers(Modifier.PUBLIC)
1602             .build())
1603         .build();
1604     assertThat(type.toString()).isEqualTo(""
1605         + "new java.lang.Runnable() {\n"
1606         + "  @java.lang.Override\n"
1607         + "  public void run() {\n"
1608         + "  }\n"
1609         + "}");
1610   }
1611 
interfaceClassToString()1612   @Test public void interfaceClassToString() throws Exception {
1613     TypeSpec type = TypeSpec.interfaceBuilder("Taco")
1614         .build();
1615     assertThat(type.toString()).isEqualTo(""
1616         + "interface Taco {\n"
1617         + "}\n");
1618   }
1619 
annotationDeclarationToString()1620   @Test public void annotationDeclarationToString() throws Exception {
1621     TypeSpec type = TypeSpec.annotationBuilder("Taco")
1622         .build();
1623     assertThat(type.toString()).isEqualTo(""
1624         + "@interface Taco {\n"
1625         + "}\n");
1626   }
1627 
toString(TypeSpec typeSpec)1628   private String toString(TypeSpec typeSpec) {
1629     return JavaFile.builder(tacosPackage, typeSpec).build().toString();
1630   }
1631 
multilineStatement()1632   @Test public void multilineStatement() throws Exception {
1633     TypeSpec taco = TypeSpec.classBuilder("Taco")
1634         .addMethod(MethodSpec.methodBuilder("toString")
1635             .addAnnotation(Override.class)
1636             .addModifiers(Modifier.PUBLIC)
1637             .returns(String.class)
1638             .addStatement("return $S\n+ $S\n+ $S\n+ $S\n+ $S",
1639                 "Taco(", "beef,", "lettuce,", "cheese", ")")
1640             .build())
1641         .build();
1642     assertThat(toString(taco)).isEqualTo(""
1643         + "package com.squareup.tacos;\n"
1644         + "\n"
1645         + "import java.lang.Override;\n"
1646         + "import java.lang.String;\n"
1647         + "\n"
1648         + "class Taco {\n"
1649         + "  @Override\n"
1650         + "  public String toString() {\n"
1651         + "    return \"Taco(\"\n"
1652         + "        + \"beef,\"\n"
1653         + "        + \"lettuce,\"\n"
1654         + "        + \"cheese\"\n"
1655         + "        + \")\";\n"
1656         + "  }\n"
1657         + "}\n");
1658   }
1659 
multilineStatementWithAnonymousClass()1660   @Test public void multilineStatementWithAnonymousClass() throws Exception {
1661     TypeName stringComparator = ParameterizedTypeName.get(Comparator.class, String.class);
1662     TypeName listOfString = ParameterizedTypeName.get(List.class, String.class);
1663     TypeSpec prefixComparator = TypeSpec.anonymousClassBuilder("")
1664         .addSuperinterface(stringComparator)
1665         .addMethod(MethodSpec.methodBuilder("compare")
1666             .addAnnotation(Override.class)
1667             .addModifiers(Modifier.PUBLIC)
1668             .returns(int.class)
1669             .addParameter(String.class, "a")
1670             .addParameter(String.class, "b")
1671             .addStatement("return a.substring(0, length)\n"
1672                 + ".compareTo(b.substring(0, length))")
1673             .build())
1674         .build();
1675     TypeSpec taco = TypeSpec.classBuilder("Taco")
1676         .addMethod(MethodSpec.methodBuilder("comparePrefix")
1677             .returns(stringComparator)
1678             .addParameter(int.class, "length", Modifier.FINAL)
1679             .addStatement("return $L", prefixComparator)
1680             .build())
1681         .addMethod(MethodSpec.methodBuilder("sortPrefix")
1682             .addParameter(listOfString, "list")
1683             .addParameter(int.class, "length", Modifier.FINAL)
1684             .addStatement("$T.sort(\nlist,\n$L)", Collections.class, prefixComparator)
1685             .build())
1686         .build();
1687     assertThat(toString(taco)).isEqualTo(""
1688         + "package com.squareup.tacos;\n"
1689         + "\n"
1690         + "import java.lang.Override;\n"
1691         + "import java.lang.String;\n"
1692         + "import java.util.Collections;\n"
1693         + "import java.util.Comparator;\n"
1694         + "import java.util.List;\n"
1695         + "\n"
1696         + "class Taco {\n"
1697         + "  Comparator<String> comparePrefix(final int length) {\n"
1698         + "    return new Comparator<String>() {\n"
1699         + "      @Override\n"
1700         + "      public int compare(String a, String b) {\n"
1701         + "        return a.substring(0, length)\n"
1702         + "            .compareTo(b.substring(0, length));\n"
1703         + "      }\n"
1704         + "    };\n"
1705         + "  }\n"
1706         + "\n"
1707         + "  void sortPrefix(List<String> list, final int length) {\n"
1708         + "    Collections.sort(\n"
1709         + "        list,\n"
1710         + "        new Comparator<String>() {\n"
1711         + "          @Override\n"
1712         + "          public int compare(String a, String b) {\n"
1713         + "            return a.substring(0, length)\n"
1714         + "                .compareTo(b.substring(0, length));\n"
1715         + "          }\n"
1716         + "        });\n"
1717         + "  }\n"
1718         + "}\n");
1719   }
1720 
multilineStrings()1721   @Test public void multilineStrings() throws Exception {
1722     TypeSpec taco = TypeSpec.classBuilder("Taco")
1723         .addField(FieldSpec.builder(String.class, "toppings")
1724             .initializer("$S", "shell\nbeef\nlettuce\ncheese\n")
1725             .build())
1726         .build();
1727     assertThat(toString(taco)).isEqualTo(""
1728         + "package com.squareup.tacos;\n"
1729         + "\n"
1730         + "import java.lang.String;\n"
1731         + "\n"
1732         + "class Taco {\n"
1733         + "  String toppings = \"shell\\n\"\n"
1734         + "      + \"beef\\n\"\n"
1735         + "      + \"lettuce\\n\"\n"
1736         + "      + \"cheese\\n\";\n"
1737         + "}\n");
1738   }
1739 
doubleFieldInitialization()1740   @Test public void doubleFieldInitialization() {
1741     try {
1742       FieldSpec.builder(String.class, "listA")
1743           .initializer("foo")
1744           .initializer("bar")
1745           .build();
1746       fail();
1747     } catch (IllegalStateException expected) {
1748     }
1749 
1750     try {
1751       FieldSpec.builder(String.class, "listA")
1752           .initializer(CodeBlock.builder().add("foo").build())
1753           .initializer(CodeBlock.builder().add("bar").build())
1754           .build();
1755       fail();
1756     } catch (IllegalStateException expected) {
1757     }
1758   }
1759 
nullAnnotationsAddition()1760   @Test public void nullAnnotationsAddition() {
1761     try {
1762       TypeSpec.classBuilder("Taco").addAnnotations(null);
1763       fail();
1764     } catch (IllegalArgumentException expected) {
1765       assertThat(expected.getMessage())
1766           .isEqualTo("annotationSpecs == null");
1767     }
1768   }
1769 
multipleAnnotationAddition()1770   @Test public void multipleAnnotationAddition() {
1771     TypeSpec taco = TypeSpec.classBuilder("Taco")
1772         .addAnnotations(Arrays.asList(
1773             AnnotationSpec.builder(SuppressWarnings.class)
1774                 .addMember("value", "$S", "unchecked")
1775                 .build(),
1776             AnnotationSpec.builder(Deprecated.class).build()))
1777         .build();
1778     assertThat(toString(taco)).isEqualTo(""
1779         + "package com.squareup.tacos;\n"
1780         + "\n"
1781         + "import java.lang.Deprecated;\n"
1782         + "import java.lang.SuppressWarnings;\n"
1783         + "\n"
1784         + "@SuppressWarnings(\"unchecked\")\n"
1785         + "@Deprecated\n"
1786         + "class Taco {\n"
1787         + "}\n");
1788   }
1789 
nullFieldsAddition()1790   @Test public void nullFieldsAddition() {
1791     try {
1792       TypeSpec.classBuilder("Taco").addFields(null);
1793       fail();
1794     } catch (IllegalArgumentException expected) {
1795       assertThat(expected.getMessage())
1796           .isEqualTo("fieldSpecs == null");
1797     }
1798   }
1799 
multipleFieldAddition()1800   @Test public void multipleFieldAddition() {
1801     TypeSpec taco = TypeSpec.classBuilder("Taco")
1802         .addFields(Arrays.asList(
1803             FieldSpec.builder(int.class, "ANSWER", Modifier.STATIC, Modifier.FINAL).build(),
1804             FieldSpec.builder(BigDecimal.class, "price", Modifier.PRIVATE).build()))
1805         .build();
1806     assertThat(toString(taco)).isEqualTo(""
1807         + "package com.squareup.tacos;\n"
1808         + "\n"
1809         + "import java.math.BigDecimal;\n"
1810         + "\n"
1811         + "class Taco {\n"
1812         + "  static final int ANSWER;\n"
1813         + "\n"
1814         + "  private BigDecimal price;\n"
1815         + "}\n");
1816   }
1817 
nullMethodsAddition()1818   @Test public void nullMethodsAddition() {
1819     try {
1820       TypeSpec.classBuilder("Taco").addMethods(null);
1821       fail();
1822     } catch (IllegalArgumentException expected) {
1823       assertThat(expected.getMessage())
1824           .isEqualTo("methodSpecs == null");
1825     }
1826   }
1827 
multipleMethodAddition()1828   @Test public void multipleMethodAddition() {
1829     TypeSpec taco = TypeSpec.classBuilder("Taco")
1830         .addMethods(Arrays.asList(
1831             MethodSpec.methodBuilder("getAnswer")
1832                 .addModifiers(Modifier.PUBLIC, Modifier.STATIC)
1833                 .returns(int.class)
1834                 .addStatement("return $L", 42)
1835                 .build(),
1836             MethodSpec.methodBuilder("getRandomQuantity")
1837                 .addModifiers(Modifier.PUBLIC)
1838                 .returns(int.class)
1839                 .addJavadoc("chosen by fair dice roll ;)")
1840                 .addStatement("return $L", 4)
1841                 .build()))
1842         .build();
1843     assertThat(toString(taco)).isEqualTo(""
1844         + "package com.squareup.tacos;\n"
1845         + "\n"
1846         + "class Taco {\n"
1847         + "  public static int getAnswer() {\n"
1848         + "    return 42;\n"
1849         + "  }\n"
1850         + "\n"
1851         + "  /**\n"
1852         + "   * chosen by fair dice roll ;)\n"
1853         + "   */\n"
1854         + "  public int getRandomQuantity() {\n"
1855         + "    return 4;\n"
1856         + "  }\n"
1857         + "}\n");
1858   }
1859 
nullSuperinterfacesAddition()1860   @Test public void nullSuperinterfacesAddition() {
1861     try {
1862       TypeSpec.classBuilder("Taco").addSuperinterfaces(null);
1863       fail();
1864     } catch (IllegalArgumentException expected) {
1865       assertThat(expected.getMessage())
1866           .isEqualTo("superinterfaces == null");
1867     }
1868   }
1869 
nullSingleSuperinterfaceAddition()1870   @Test public void nullSingleSuperinterfaceAddition() {
1871     try {
1872       TypeSpec.classBuilder("Taco").addSuperinterface((TypeName) null);
1873       fail();
1874     } catch (IllegalArgumentException expected) {
1875       assertThat(expected.getMessage())
1876           .isEqualTo("superinterface == null");
1877     }
1878   }
1879 
nullInSuperinterfaceIterableAddition()1880   @Test public void nullInSuperinterfaceIterableAddition() {
1881     List<TypeName> superinterfaces = new ArrayList<>();
1882     superinterfaces.add(TypeName.get(List.class));
1883     superinterfaces.add(null);
1884 
1885     try {
1886       TypeSpec.classBuilder("Taco").addSuperinterfaces(superinterfaces);
1887       fail();
1888     } catch (IllegalArgumentException expected) {
1889       assertThat(expected.getMessage())
1890           .isEqualTo("superinterface == null");
1891     }
1892   }
1893 
multipleSuperinterfaceAddition()1894   @Test public void multipleSuperinterfaceAddition() {
1895     TypeSpec taco = TypeSpec.classBuilder("Taco")
1896         .addSuperinterfaces(Arrays.asList(
1897             TypeName.get(Serializable.class),
1898             TypeName.get(EventListener.class)))
1899         .build();
1900     assertThat(toString(taco)).isEqualTo(""
1901         + "package com.squareup.tacos;\n"
1902         + "\n"
1903         + "import java.io.Serializable;\n"
1904         + "import java.util.EventListener;\n"
1905         + "\n"
1906         + "class Taco implements Serializable, EventListener {\n"
1907         + "}\n");
1908   }
1909 
nullModifiersAddition()1910   @Test public void nullModifiersAddition() {
1911     try {
1912       TypeSpec.classBuilder("Taco").addModifiers((Modifier) null).build();
1913       fail();
1914     } catch(IllegalArgumentException expected) {
1915       assertThat(expected.getMessage())
1916           .isEqualTo("modifiers contain null");
1917     }
1918   }
1919 
nullTypeVariablesAddition()1920   @Test public void nullTypeVariablesAddition() {
1921     try {
1922       TypeSpec.classBuilder("Taco").addTypeVariables(null);
1923       fail();
1924     } catch (IllegalArgumentException expected) {
1925       assertThat(expected.getMessage())
1926           .isEqualTo("typeVariables == null");
1927     }
1928   }
1929 
multipleTypeVariableAddition()1930   @Test public void multipleTypeVariableAddition() {
1931     TypeSpec location = TypeSpec.classBuilder("Location")
1932         .addTypeVariables(Arrays.asList(
1933             TypeVariableName.get("T"),
1934             TypeVariableName.get("P", Number.class)))
1935         .build();
1936     assertThat(toString(location)).isEqualTo(""
1937         + "package com.squareup.tacos;\n"
1938         + "\n"
1939         + "import java.lang.Number;\n"
1940         + "\n"
1941         + "class Location<T, P extends Number> {\n"
1942         + "}\n");
1943   }
1944 
nullTypesAddition()1945   @Test public void nullTypesAddition() {
1946     try {
1947       TypeSpec.classBuilder("Taco").addTypes(null);
1948       fail();
1949     } catch (IllegalArgumentException expected) {
1950       assertThat(expected.getMessage())
1951           .isEqualTo("typeSpecs == null");
1952     }
1953   }
1954 
multipleTypeAddition()1955   @Test public void multipleTypeAddition() {
1956     TypeSpec taco = TypeSpec.classBuilder("Taco")
1957         .addTypes(Arrays.asList(
1958             TypeSpec.classBuilder("Topping").build(),
1959             TypeSpec.classBuilder("Sauce").build()))
1960         .build();
1961     assertThat(toString(taco)).isEqualTo(""
1962         + "package com.squareup.tacos;\n"
1963         + "\n"
1964         + "class Taco {\n"
1965         + "  class Topping {\n"
1966         + "  }\n"
1967         + "\n"
1968         + "  class Sauce {\n"
1969         + "  }\n"
1970         + "}\n");
1971   }
1972 
tryCatch()1973   @Test public void tryCatch() {
1974     TypeSpec taco = TypeSpec.classBuilder("Taco")
1975         .addMethod(MethodSpec.methodBuilder("addTopping")
1976             .addParameter(ClassName.get("com.squareup.tacos", "Topping"), "topping")
1977             .beginControlFlow("try")
1978             .addCode("/* do something tricky with the topping */\n")
1979             .nextControlFlow("catch ($T e)",
1980                 ClassName.get("com.squareup.tacos", "IllegalToppingException"))
1981             .endControlFlow()
1982             .build())
1983         .build();
1984     assertThat(toString(taco)).isEqualTo(""
1985         + "package com.squareup.tacos;\n"
1986         + "\n"
1987         + "class Taco {\n"
1988         + "  void addTopping(Topping topping) {\n"
1989         + "    try {\n"
1990         + "      /* do something tricky with the topping */\n"
1991         + "    } catch (IllegalToppingException e) {\n"
1992         + "    }\n"
1993         + "  }\n"
1994         + "}\n");
1995   }
1996 
ifElse()1997   @Test public void ifElse() {
1998     TypeSpec taco = TypeSpec.classBuilder("Taco")
1999         .addMethod(
2000             MethodSpec.methodBuilder("isDelicious")
2001                 .addParameter(TypeName.INT, "count")
2002                 .returns(TypeName.BOOLEAN)
2003                 .beginControlFlow("if (count > 0)")
2004                 .addStatement("return true")
2005                 .nextControlFlow("else")
2006                 .addStatement("return false")
2007                 .endControlFlow()
2008                 .build()
2009         )
2010         .build();
2011     assertThat(toString(taco)).isEqualTo(""
2012         + "package com.squareup.tacos;\n"
2013         + "\n"
2014         + "class Taco {\n"
2015         + "  boolean isDelicious(int count) {\n"
2016         + "    if (count > 0) {\n"
2017         + "      return true;\n"
2018         + "    } else {\n"
2019         + "      return false;\n"
2020         + "    }\n"
2021         + "  }\n"
2022         + "}\n");
2023   }
2024 
literalFromAnything()2025   @Test public void literalFromAnything() {
2026     Object value = new Object() {
2027       @Override public String toString() {
2028         return "foo";
2029       }
2030     };
2031     assertThat(CodeBlock.of("$L", value).toString()).isEqualTo("foo");
2032   }
2033 
nameFromCharSequence()2034   @Test public void nameFromCharSequence() {
2035     assertThat(CodeBlock.of("$N", "text").toString()).isEqualTo("text");
2036   }
2037 
nameFromField()2038   @Test public void nameFromField() {
2039     FieldSpec field = FieldSpec.builder(String.class, "field").build();
2040     assertThat(CodeBlock.of("$N", field).toString()).isEqualTo("field");
2041   }
2042 
nameFromParameter()2043   @Test public void nameFromParameter() {
2044     ParameterSpec parameter = ParameterSpec.builder(String.class, "parameter").build();
2045     assertThat(CodeBlock.of("$N", parameter).toString()).isEqualTo("parameter");
2046   }
2047 
nameFromMethod()2048   @Test public void nameFromMethod() {
2049     MethodSpec method = MethodSpec.methodBuilder("method")
2050         .addModifiers(Modifier.ABSTRACT)
2051         .returns(String.class)
2052         .build();
2053     assertThat(CodeBlock.of("$N", method).toString()).isEqualTo("method");
2054   }
2055 
nameFromType()2056   @Test public void nameFromType() {
2057     TypeSpec type = TypeSpec.classBuilder("Type").build();
2058     assertThat(CodeBlock.of("$N", type).toString()).isEqualTo("Type");
2059   }
2060 
nameFromUnsupportedType()2061   @Test public void nameFromUnsupportedType() {
2062     try {
2063       CodeBlock.builder().add("$N", String.class);
2064       fail();
2065     } catch (IllegalArgumentException expected) {
2066       assertThat(expected).hasMessageThat().isEqualTo("expected name but was " + String.class);
2067     }
2068   }
2069 
stringFromAnything()2070   @Test public void stringFromAnything() {
2071     Object value = new Object() {
2072       @Override public String toString() {
2073         return "foo";
2074       }
2075     };
2076     assertThat(CodeBlock.of("$S", value).toString()).isEqualTo("\"foo\"");
2077   }
2078 
stringFromNull()2079   @Test public void stringFromNull() {
2080     assertThat(CodeBlock.of("$S", new Object[] {null}).toString()).isEqualTo("null");
2081   }
2082 
typeFromTypeName()2083   @Test public void typeFromTypeName() {
2084     TypeName typeName = TypeName.get(String.class);
2085     assertThat(CodeBlock.of("$T", typeName).toString()).isEqualTo("java.lang.String");
2086   }
2087 
typeFromTypeMirror()2088   @Test public void typeFromTypeMirror() {
2089     TypeMirror mirror = getElement(String.class).asType();
2090     assertThat(CodeBlock.of("$T", mirror).toString()).isEqualTo("java.lang.String");
2091   }
2092 
typeFromTypeElement()2093   @Test public void typeFromTypeElement() {
2094     TypeElement element = getElement(String.class);
2095     assertThat(CodeBlock.of("$T", element).toString()).isEqualTo("java.lang.String");
2096   }
2097 
typeFromReflectType()2098   @Test public void typeFromReflectType() {
2099     assertThat(CodeBlock.of("$T", String.class).toString()).isEqualTo("java.lang.String");
2100   }
2101 
typeFromUnsupportedType()2102   @Test public void typeFromUnsupportedType() {
2103     try {
2104       CodeBlock.builder().add("$T", "java.lang.String");
2105       fail();
2106     } catch (IllegalArgumentException expected) {
2107       assertThat(expected).hasMessageThat().isEqualTo("expected type but was java.lang.String");
2108     }
2109   }
2110 
tooFewArguments()2111   @Test public void tooFewArguments() {
2112     try {
2113       CodeBlock.builder().add("$S");
2114       fail();
2115     } catch (IllegalArgumentException expected) {
2116       assertThat(expected).hasMessageThat().isEqualTo("index 1 for '$S' not in range (received 0 arguments)");
2117     }
2118   }
2119 
unusedArgumentsRelative()2120   @Test public void unusedArgumentsRelative() {
2121     try {
2122       CodeBlock.builder().add("$L $L", "a", "b", "c");
2123       fail();
2124     } catch (IllegalArgumentException expected) {
2125       assertThat(expected).hasMessageThat().isEqualTo("unused arguments: expected 2, received 3");
2126     }
2127   }
2128 
unusedArgumentsIndexed()2129   @Test public void unusedArgumentsIndexed() {
2130     try {
2131       CodeBlock.builder().add("$1L $2L", "a", "b", "c");
2132       fail();
2133     } catch (IllegalArgumentException expected) {
2134       assertThat(expected).hasMessageThat().isEqualTo("unused argument: $3");
2135     }
2136     try {
2137       CodeBlock.builder().add("$1L $1L $1L", "a", "b", "c");
2138       fail();
2139     } catch (IllegalArgumentException expected) {
2140       assertThat(expected).hasMessageThat().isEqualTo("unused arguments: $2, $3");
2141     }
2142     try {
2143       CodeBlock.builder().add("$3L $1L $3L $1L $3L", "a", "b", "c", "d");
2144       fail();
2145     } catch (IllegalArgumentException expected) {
2146       assertThat(expected).hasMessageThat().isEqualTo("unused arguments: $2, $4");
2147     }
2148   }
2149 
superClassOnlyValidForClasses()2150   @Test public void superClassOnlyValidForClasses() {
2151     try {
2152       TypeSpec.annotationBuilder("A").superclass(ClassName.get(Object.class));
2153       fail();
2154     } catch (IllegalStateException expected) {
2155     }
2156     try {
2157       TypeSpec.enumBuilder("E").superclass(ClassName.get(Object.class));
2158       fail();
2159     } catch (IllegalStateException expected) {
2160     }
2161     try {
2162       TypeSpec.interfaceBuilder("I").superclass(ClassName.get(Object.class));
2163       fail();
2164     } catch (IllegalStateException expected) {
2165     }
2166   }
2167 
invalidSuperClass()2168   @Test public void invalidSuperClass() {
2169     try {
2170       TypeSpec.classBuilder("foo")
2171           .superclass(ClassName.get(List.class))
2172           .superclass(ClassName.get(Map.class));
2173       fail();
2174     } catch (IllegalStateException expected) {
2175     }
2176     try {
2177       TypeSpec.classBuilder("foo")
2178           .superclass(TypeName.INT);
2179       fail();
2180     } catch (IllegalArgumentException expected) {
2181     }
2182   }
2183 
staticCodeBlock()2184   @Test public void staticCodeBlock() {
2185     TypeSpec taco = TypeSpec.classBuilder("Taco")
2186         .addField(String.class, "foo", Modifier.PRIVATE)
2187         .addField(String.class, "FOO", Modifier.PRIVATE, Modifier.STATIC, Modifier.FINAL)
2188         .addStaticBlock(CodeBlock.builder()
2189             .addStatement("FOO = $S", "FOO")
2190             .build())
2191         .addMethod(MethodSpec.methodBuilder("toString")
2192             .addAnnotation(Override.class)
2193             .addModifiers(Modifier.PUBLIC)
2194             .returns(String.class)
2195             .addCode("return FOO;\n")
2196             .build())
2197         .build();
2198     assertThat(toString(taco)).isEqualTo(""
2199         + "package com.squareup.tacos;\n"
2200         + "\n"
2201         + "import java.lang.Override;\n"
2202         + "import java.lang.String;\n"
2203         + "\n"
2204         + "class Taco {\n"
2205         + "  private static final String FOO;\n"
2206         + "\n"
2207         + "  static {\n"
2208         + "    FOO = \"FOO\";\n"
2209         + "  }\n"
2210         + "\n"
2211         + "  private String foo;\n"
2212         + "\n"
2213         + "  @Override\n"
2214         + "  public String toString() {\n"
2215         + "    return FOO;\n"
2216         + "  }\n"
2217         + "}\n");
2218   }
2219 
initializerBlockInRightPlace()2220   @Test public void initializerBlockInRightPlace() {
2221     TypeSpec taco = TypeSpec.classBuilder("Taco")
2222         .addField(String.class, "foo", Modifier.PRIVATE)
2223         .addField(String.class, "FOO", Modifier.PRIVATE, Modifier.STATIC, Modifier.FINAL)
2224         .addStaticBlock(CodeBlock.builder()
2225             .addStatement("FOO = $S", "FOO")
2226             .build())
2227         .addMethod(MethodSpec.constructorBuilder().build())
2228         .addMethod(MethodSpec.methodBuilder("toString")
2229             .addAnnotation(Override.class)
2230             .addModifiers(Modifier.PUBLIC)
2231             .returns(String.class)
2232             .addCode("return FOO;\n")
2233             .build())
2234         .addInitializerBlock(CodeBlock.builder()
2235             .addStatement("foo = $S", "FOO")
2236             .build())
2237         .build();
2238     assertThat(toString(taco)).isEqualTo(""
2239         + "package com.squareup.tacos;\n"
2240         + "\n"
2241         + "import java.lang.Override;\n"
2242         + "import java.lang.String;\n"
2243         + "\n"
2244         + "class Taco {\n"
2245         + "  private static final String FOO;\n"
2246         + "\n"
2247         + "  static {\n"
2248         + "    FOO = \"FOO\";\n"
2249         + "  }\n"
2250         + "\n"
2251         + "  private String foo;\n"
2252         + "\n"
2253         + "  {\n"
2254         + "    foo = \"FOO\";\n"
2255         + "  }\n"
2256         + "\n"
2257         + "  Taco() {\n"
2258         + "  }\n"
2259         + "\n"
2260         + "  @Override\n"
2261         + "  public String toString() {\n"
2262         + "    return FOO;\n"
2263         + "  }\n"
2264         + "}\n");
2265   }
2266 
initializersToBuilder()2267   @Test public void initializersToBuilder() {
2268     // Tests if toBuilder() contains correct static and instance initializers
2269     Element originatingElement = getElement(TypeSpecTest.class);
2270     TypeSpec taco = TypeSpec.classBuilder("Taco")
2271         .addField(String.class, "foo", Modifier.PRIVATE)
2272         .addField(String.class, "FOO", Modifier.PRIVATE, Modifier.STATIC, Modifier.FINAL)
2273         .addStaticBlock(CodeBlock.builder()
2274             .addStatement("FOO = $S", "FOO")
2275             .build())
2276         .addMethod(MethodSpec.constructorBuilder().build())
2277         .addMethod(MethodSpec.methodBuilder("toString")
2278             .addAnnotation(Override.class)
2279             .addModifiers(Modifier.PUBLIC)
2280             .returns(String.class)
2281             .addCode("return FOO;\n")
2282             .build())
2283         .addInitializerBlock(CodeBlock.builder()
2284             .addStatement("foo = $S", "FOO")
2285             .build())
2286         .addOriginatingElement(originatingElement)
2287         .alwaysQualify("com.example.AlwaysQualified")
2288         .build();
2289 
2290     TypeSpec recreatedTaco = taco.toBuilder().build();
2291     assertThat(toString(taco)).isEqualTo(toString(recreatedTaco));
2292     assertThat(taco.originatingElements)
2293         .containsExactlyElementsIn(recreatedTaco.originatingElements);
2294     assertThat(taco.alwaysQualifiedNames)
2295         .containsExactlyElementsIn(recreatedTaco.alwaysQualifiedNames);
2296 
2297     TypeSpec initializersAdded = taco.toBuilder()
2298         .addInitializerBlock(CodeBlock.builder()
2299             .addStatement("foo = $S", "instanceFoo")
2300             .build())
2301         .addStaticBlock(CodeBlock.builder()
2302             .addStatement("FOO = $S", "staticFoo")
2303             .build())
2304         .build();
2305 
2306     assertThat(toString(initializersAdded)).isEqualTo(""
2307         + "package com.squareup.tacos;\n"
2308         + "\n"
2309         + "import java.lang.Override;\n"
2310         + "import java.lang.String;\n"
2311         + "\n"
2312         + "class Taco {\n"
2313         + "  private static final String FOO;\n"
2314         + "\n"
2315         + "  static {\n"
2316         + "    FOO = \"FOO\";\n"
2317         + "  }\n"
2318         + "  static {\n"
2319         + "    FOO = \"staticFoo\";\n"
2320         + "  }\n"
2321         + "\n"
2322         + "  private String foo;\n"
2323         + "\n"
2324         + "  {\n"
2325         + "    foo = \"FOO\";\n"
2326         + "  }\n"
2327         + "  {\n"
2328         + "    foo = \"instanceFoo\";\n"
2329         + "  }\n"
2330         + "\n"
2331         + "  Taco() {\n"
2332         + "  }\n"
2333         + "\n"
2334         + "  @Override\n"
2335         + "  public String toString() {\n"
2336         + "    return FOO;\n"
2337         + "  }\n"
2338         + "}\n");
2339   }
2340 
initializerBlockUnsupportedExceptionOnInterface()2341   @Test public void initializerBlockUnsupportedExceptionOnInterface() {
2342     TypeSpec.Builder interfaceBuilder = TypeSpec.interfaceBuilder("Taco");
2343     try {
2344       interfaceBuilder.addInitializerBlock(CodeBlock.builder().build());
2345       fail("Exception expected");
2346     } catch (UnsupportedOperationException e) {
2347     }
2348   }
2349 
initializerBlockUnsupportedExceptionOnAnnotation()2350   @Test public void initializerBlockUnsupportedExceptionOnAnnotation() {
2351     TypeSpec.Builder annotationBuilder = TypeSpec.annotationBuilder("Taco");
2352     try {
2353       annotationBuilder.addInitializerBlock(CodeBlock.builder().build());
2354       fail("Exception expected");
2355     } catch (UnsupportedOperationException e) {
2356     }
2357   }
2358 
lineWrapping()2359   @Test public void lineWrapping() {
2360     MethodSpec.Builder methodBuilder = MethodSpec.methodBuilder("call");
2361     methodBuilder.addCode("$[call(");
2362     for (int i = 0; i < 32; i++) {
2363       methodBuilder.addParameter(String.class, "s" + i);
2364       methodBuilder.addCode(i > 0 ? ",$W$S" : "$S", i);
2365     }
2366     methodBuilder.addCode(");$]\n");
2367 
2368     TypeSpec taco = TypeSpec.classBuilder("Taco")
2369         .addMethod(methodBuilder.build())
2370         .build();
2371     assertThat(toString(taco)).isEqualTo(""
2372         + "package com.squareup.tacos;\n"
2373         + "\n"
2374         + "import java.lang.String;\n"
2375         + "\n"
2376         + "class Taco {\n"
2377         + "  void call(String s0, String s1, String s2, String s3, String s4, String s5, String s6, String s7,\n"
2378         + "      String s8, String s9, String s10, String s11, String s12, String s13, String s14, String s15,\n"
2379         + "      String s16, String s17, String s18, String s19, String s20, String s21, String s22,\n"
2380         + "      String s23, String s24, String s25, String s26, String s27, String s28, String s29,\n"
2381         + "      String s30, String s31) {\n"
2382         + "    call(\"0\", \"1\", \"2\", \"3\", \"4\", \"5\", \"6\", \"7\", \"8\", \"9\", \"10\", \"11\", \"12\", \"13\", \"14\", \"15\", \"16\",\n"
2383         + "        \"17\", \"18\", \"19\", \"20\", \"21\", \"22\", \"23\", \"24\", \"25\", \"26\", \"27\", \"28\", \"29\", \"30\", \"31\");\n"
2384         + "  }\n"
2385         + "}\n");
2386   }
2387 
lineWrappingWithZeroWidthSpace()2388   @Test public void lineWrappingWithZeroWidthSpace() {
2389     MethodSpec method = MethodSpec.methodBuilder("call")
2390         .addCode("$[iAmSickOfWaitingInLine($Z")
2391         .addCode("it, has, been, far, too, long, of, a, wait, and, i, would, like, to, eat, ")
2392         .addCode("this, is, a, run, on, sentence")
2393         .addCode(");$]\n")
2394         .build();
2395 
2396     TypeSpec taco = TypeSpec.classBuilder("Taco")
2397         .addMethod(method)
2398         .build();
2399     assertThat(toString(taco)).isEqualTo(""
2400         + "package com.squareup.tacos;\n"
2401         + "\n"
2402         + "class Taco {\n"
2403         + "  void call() {\n"
2404         + "    iAmSickOfWaitingInLine(\n"
2405         + "        it, has, been, far, too, long, of, a, wait, and, i, would, like, to, eat, this, is, a, run, on, sentence);\n"
2406         + "  }\n"
2407         + "}\n");
2408   }
2409 
equalsAndHashCode()2410   @Test public void equalsAndHashCode() {
2411     TypeSpec a = TypeSpec.interfaceBuilder("taco").build();
2412     TypeSpec b = TypeSpec.interfaceBuilder("taco").build();
2413     assertThat(a.equals(b)).isTrue();
2414     assertThat(a.hashCode()).isEqualTo(b.hashCode());
2415     a = TypeSpec.classBuilder("taco").build();
2416     b = TypeSpec.classBuilder("taco").build();
2417     assertThat(a.equals(b)).isTrue();
2418     assertThat(a.hashCode()).isEqualTo(b.hashCode());
2419     a = TypeSpec.enumBuilder("taco").addEnumConstant("SALSA").build();
2420     b = TypeSpec.enumBuilder("taco").addEnumConstant("SALSA").build();
2421     assertThat(a.equals(b)).isTrue();
2422     assertThat(a.hashCode()).isEqualTo(b.hashCode());
2423     a = TypeSpec.annotationBuilder("taco").build();
2424     b = TypeSpec.annotationBuilder("taco").build();
2425     assertThat(a.equals(b)).isTrue();
2426     assertThat(a.hashCode()).isEqualTo(b.hashCode());
2427   }
2428 
classNameFactories()2429   @Test public void classNameFactories() {
2430     ClassName className = ClassName.get("com.example", "Example");
2431     assertThat(TypeSpec.classBuilder(className).build().name).isEqualTo("Example");
2432     assertThat(TypeSpec.interfaceBuilder(className).build().name).isEqualTo("Example");
2433     assertThat(TypeSpec.enumBuilder(className).addEnumConstant("A").build().name).isEqualTo("Example");
2434     assertThat(TypeSpec.annotationBuilder(className).build().name).isEqualTo("Example");
2435   }
2436 
2437   @Test
modifyAnnotations()2438   public void modifyAnnotations() {
2439     TypeSpec.Builder builder =
2440         TypeSpec.classBuilder("Taco")
2441             .addAnnotation(Override.class)
2442             .addAnnotation(SuppressWarnings.class);
2443 
2444     builder.annotations.remove(1);
2445     assertThat(builder.build().annotations).hasSize(1);
2446   }
2447 
2448   @Test
modifyModifiers()2449   public void modifyModifiers() {
2450     TypeSpec.Builder builder =
2451         TypeSpec.classBuilder("Taco").addModifiers(Modifier.PUBLIC, Modifier.FINAL);
2452 
2453     builder.modifiers.remove(1);
2454     assertThat(builder.build().modifiers).containsExactly(Modifier.PUBLIC);
2455   }
2456 
2457   @Test
modifyFields()2458   public void modifyFields() {
2459     TypeSpec.Builder builder = TypeSpec.classBuilder("Taco")
2460         .addField(int.class, "source");
2461 
2462     builder.fieldSpecs.remove(0);
2463     assertThat(builder.build().fieldSpecs).isEmpty();
2464   }
2465 
2466   @Test
modifyTypeVariables()2467   public void modifyTypeVariables() {
2468     TypeVariableName t = TypeVariableName.get("T");
2469     TypeSpec.Builder builder =
2470         TypeSpec.classBuilder("Taco")
2471             .addTypeVariable(t)
2472             .addTypeVariable(TypeVariableName.get("V"));
2473 
2474     builder.typeVariables.remove(1);
2475     assertThat(builder.build().typeVariables).containsExactly(t);
2476   }
2477 
2478   @Test
modifySuperinterfaces()2479   public void modifySuperinterfaces() {
2480     TypeSpec.Builder builder = TypeSpec.classBuilder("Taco")
2481         .addSuperinterface(File.class);
2482 
2483     builder.superinterfaces.clear();
2484     assertThat(builder.build().superinterfaces).isEmpty();
2485   }
2486 
2487   @Test
modifyMethods()2488   public void modifyMethods() {
2489     TypeSpec.Builder builder = TypeSpec.classBuilder("Taco")
2490         .addMethod(MethodSpec.methodBuilder("bell").build());
2491 
2492     builder.methodSpecs.clear();
2493     assertThat(builder.build().methodSpecs).isEmpty();
2494   }
2495 
2496   @Test
modifyTypes()2497   public void modifyTypes() {
2498     TypeSpec.Builder builder = TypeSpec.classBuilder("Taco")
2499         .addType(TypeSpec.classBuilder("Bell").build());
2500 
2501     builder.typeSpecs.clear();
2502     assertThat(builder.build().typeSpecs).isEmpty();
2503   }
2504 
2505   @Test
modifyEnumConstants()2506   public void modifyEnumConstants() {
2507     TypeSpec constantType = TypeSpec.anonymousClassBuilder("").build();
2508     TypeSpec.Builder builder = TypeSpec.enumBuilder("Taco")
2509         .addEnumConstant("BELL", constantType)
2510         .addEnumConstant("WUT", TypeSpec.anonymousClassBuilder("").build());
2511 
2512     builder.enumConstants.remove("WUT");
2513     assertThat(builder.build().enumConstants).containsExactly("BELL", constantType);
2514   }
2515 
2516   @Test
modifyOriginatingElements()2517   public void modifyOriginatingElements() {
2518     TypeSpec.Builder builder = TypeSpec.classBuilder("Taco")
2519         .addOriginatingElement(Mockito.mock(Element.class));
2520 
2521     builder.originatingElements.clear();
2522     assertThat(builder.build().originatingElements).isEmpty();
2523   }
2524 
javadocWithTrailingLineDoesNotAddAnother()2525   @Test public void javadocWithTrailingLineDoesNotAddAnother() {
2526     TypeSpec spec = TypeSpec.classBuilder("Taco")
2527         .addJavadoc("Some doc with a newline\n")
2528         .build();
2529 
2530     assertThat(toString(spec)).isEqualTo(""
2531         + "package com.squareup.tacos;\n"
2532         + "\n"
2533         + "/**\n"
2534         + " * Some doc with a newline\n"
2535         + " */\n"
2536         + "class Taco {\n"
2537         + "}\n");
2538   }
2539 
javadocEnsuresTrailingLine()2540   @Test public void javadocEnsuresTrailingLine() {
2541     TypeSpec spec = TypeSpec.classBuilder("Taco")
2542         .addJavadoc("Some doc with a newline")
2543         .build();
2544 
2545     assertThat(toString(spec)).isEqualTo(""
2546         + "package com.squareup.tacos;\n"
2547         + "\n"
2548         + "/**\n"
2549         + " * Some doc with a newline\n"
2550         + " */\n"
2551         + "class Taco {\n"
2552         + "}\n");
2553   }
2554 }
2555