• 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 java.io.IOException;
19 import java.lang.reflect.ParameterizedType;
20 import java.lang.reflect.Type;
21 import java.util.ArrayList;
22 import java.util.Arrays;
23 import java.util.Collections;
24 import java.util.EnumSet;
25 import java.util.HashSet;
26 import java.util.Iterator;
27 import java.util.LinkedHashMap;
28 import java.util.LinkedHashSet;
29 import java.util.List;
30 import java.util.Locale;
31 import java.util.Map;
32 import java.util.Set;
33 import javax.lang.model.SourceVersion;
34 import javax.lang.model.element.Element;
35 import javax.lang.model.element.Modifier;
36 import javax.lang.model.element.TypeElement;
37 import javax.lang.model.type.DeclaredType;
38 import javax.lang.model.type.NoType;
39 import javax.lang.model.type.TypeMirror;
40 import javax.lang.model.util.ElementFilter;
41 
42 import static com.squareup.javapoet.Util.checkArgument;
43 import static com.squareup.javapoet.Util.checkNotNull;
44 import static com.squareup.javapoet.Util.checkState;
45 import static com.squareup.javapoet.Util.requireExactlyOneOf;
46 
47 /** A generated class, interface, or enum declaration. */
48 public final class TypeSpec {
49   public final Kind kind;
50   public final String name;
51   public final CodeBlock anonymousTypeArguments;
52   public final CodeBlock javadoc;
53   public final List<AnnotationSpec> annotations;
54   public final Set<Modifier> modifiers;
55   public final List<TypeVariableName> typeVariables;
56   public final TypeName superclass;
57   public final List<TypeName> superinterfaces;
58   public final Map<String, TypeSpec> enumConstants;
59   public final List<FieldSpec> fieldSpecs;
60   public final CodeBlock staticBlock;
61   public final CodeBlock initializerBlock;
62   public final List<MethodSpec> methodSpecs;
63   public final List<TypeSpec> typeSpecs;
64   final Set<String> nestedTypesSimpleNames;
65   public final List<Element> originatingElements;
66   public final Set<String> alwaysQualifiedNames;
67 
TypeSpec(Builder builder)68   private TypeSpec(Builder builder) {
69     this.kind = builder.kind;
70     this.name = builder.name;
71     this.anonymousTypeArguments = builder.anonymousTypeArguments;
72     this.javadoc = builder.javadoc.build();
73     this.annotations = Util.immutableList(builder.annotations);
74     this.modifiers = Util.immutableSet(builder.modifiers);
75     this.typeVariables = Util.immutableList(builder.typeVariables);
76     this.superclass = builder.superclass;
77     this.superinterfaces = Util.immutableList(builder.superinterfaces);
78     this.enumConstants = Util.immutableMap(builder.enumConstants);
79     this.fieldSpecs = Util.immutableList(builder.fieldSpecs);
80     this.staticBlock = builder.staticBlock.build();
81     this.initializerBlock = builder.initializerBlock.build();
82     this.methodSpecs = Util.immutableList(builder.methodSpecs);
83     this.typeSpecs = Util.immutableList(builder.typeSpecs);
84     this.alwaysQualifiedNames = Util.immutableSet(builder.alwaysQualifiedNames);
85 
86     nestedTypesSimpleNames = new HashSet<>(builder.typeSpecs.size());
87     List<Element> originatingElementsMutable = new ArrayList<>();
88     originatingElementsMutable.addAll(builder.originatingElements);
89     for (TypeSpec typeSpec : builder.typeSpecs) {
90       nestedTypesSimpleNames.add(typeSpec.name);
91       originatingElementsMutable.addAll(typeSpec.originatingElements);
92     }
93 
94     this.originatingElements = Util.immutableList(originatingElementsMutable);
95   }
96 
97   /**
98    * Creates a dummy type spec for type-resolution only (in CodeWriter)
99    * while emitting the type declaration but before entering the type body.
100    */
TypeSpec(TypeSpec type)101   private TypeSpec(TypeSpec type) {
102     assert type.anonymousTypeArguments == null;
103     this.kind = type.kind;
104     this.name = type.name;
105     this.anonymousTypeArguments = null;
106     this.javadoc = type.javadoc;
107     this.annotations = Collections.emptyList();
108     this.modifiers = Collections.emptySet();
109     this.typeVariables = Collections.emptyList();
110     this.superclass = null;
111     this.superinterfaces = Collections.emptyList();
112     this.enumConstants = Collections.emptyMap();
113     this.fieldSpecs = Collections.emptyList();
114     this.staticBlock = type.staticBlock;
115     this.initializerBlock = type.initializerBlock;
116     this.methodSpecs = Collections.emptyList();
117     this.typeSpecs = Collections.emptyList();
118     this.originatingElements = Collections.emptyList();
119     this.nestedTypesSimpleNames = Collections.emptySet();
120     this.alwaysQualifiedNames = Collections.emptySet();
121   }
122 
hasModifier(Modifier modifier)123   public boolean hasModifier(Modifier modifier) {
124     return modifiers.contains(modifier);
125   }
126 
classBuilder(String name)127   public static Builder classBuilder(String name) {
128     return new Builder(Kind.CLASS, checkNotNull(name, "name == null"), null);
129   }
130 
classBuilder(ClassName className)131   public static Builder classBuilder(ClassName className) {
132     return classBuilder(checkNotNull(className, "className == null").simpleName());
133   }
134 
interfaceBuilder(String name)135   public static Builder interfaceBuilder(String name) {
136     return new Builder(Kind.INTERFACE, checkNotNull(name, "name == null"), null);
137   }
138 
interfaceBuilder(ClassName className)139   public static Builder interfaceBuilder(ClassName className) {
140     return interfaceBuilder(checkNotNull(className, "className == null").simpleName());
141   }
142 
enumBuilder(String name)143   public static Builder enumBuilder(String name) {
144     return new Builder(Kind.ENUM, checkNotNull(name, "name == null"), null);
145   }
146 
enumBuilder(ClassName className)147   public static Builder enumBuilder(ClassName className) {
148     return enumBuilder(checkNotNull(className, "className == null").simpleName());
149   }
150 
anonymousClassBuilder(String typeArgumentsFormat, Object... args)151   public static Builder anonymousClassBuilder(String typeArgumentsFormat, Object... args) {
152     return anonymousClassBuilder(CodeBlock.of(typeArgumentsFormat, args));
153   }
154 
anonymousClassBuilder(CodeBlock typeArguments)155   public static Builder anonymousClassBuilder(CodeBlock typeArguments) {
156     return new Builder(Kind.CLASS, null, typeArguments);
157   }
158 
annotationBuilder(String name)159   public static Builder annotationBuilder(String name) {
160     return new Builder(Kind.ANNOTATION, checkNotNull(name, "name == null"), null);
161   }
162 
annotationBuilder(ClassName className)163   public static Builder annotationBuilder(ClassName className) {
164     return annotationBuilder(checkNotNull(className, "className == null").simpleName());
165   }
166 
toBuilder()167   public Builder toBuilder() {
168     Builder builder = new Builder(kind, name, anonymousTypeArguments);
169     builder.javadoc.add(javadoc);
170     builder.annotations.addAll(annotations);
171     builder.modifiers.addAll(modifiers);
172     builder.typeVariables.addAll(typeVariables);
173     builder.superclass = superclass;
174     builder.superinterfaces.addAll(superinterfaces);
175     builder.enumConstants.putAll(enumConstants);
176     builder.fieldSpecs.addAll(fieldSpecs);
177     builder.methodSpecs.addAll(methodSpecs);
178     builder.typeSpecs.addAll(typeSpecs);
179     builder.initializerBlock.add(initializerBlock);
180     builder.staticBlock.add(staticBlock);
181     builder.originatingElements.addAll(originatingElements);
182     builder.alwaysQualifiedNames.addAll(alwaysQualifiedNames);
183     return builder;
184   }
185 
emit(CodeWriter codeWriter, String enumName, Set<Modifier> implicitModifiers)186   void emit(CodeWriter codeWriter, String enumName, Set<Modifier> implicitModifiers)
187       throws IOException {
188     // Nested classes interrupt wrapped line indentation. Stash the current wrapping state and put
189     // it back afterwards when this type is complete.
190     int previousStatementLine = codeWriter.statementLine;
191     codeWriter.statementLine = -1;
192 
193     try {
194       if (enumName != null) {
195         codeWriter.emitJavadoc(javadoc);
196         codeWriter.emitAnnotations(annotations, false);
197         codeWriter.emit("$L", enumName);
198         if (!anonymousTypeArguments.formatParts.isEmpty()) {
199           codeWriter.emit("(");
200           codeWriter.emit(anonymousTypeArguments);
201           codeWriter.emit(")");
202         }
203         if (fieldSpecs.isEmpty() && methodSpecs.isEmpty() && typeSpecs.isEmpty()) {
204           return; // Avoid unnecessary braces "{}".
205         }
206         codeWriter.emit(" {\n");
207       } else if (anonymousTypeArguments != null) {
208         TypeName supertype = !superinterfaces.isEmpty() ? superinterfaces.get(0) : superclass;
209         codeWriter.emit("new $T(", supertype);
210         codeWriter.emit(anonymousTypeArguments);
211         codeWriter.emit(") {\n");
212       } else {
213         // Push an empty type (specifically without nested types) for type-resolution.
214         codeWriter.pushType(new TypeSpec(this));
215 
216         codeWriter.emitJavadoc(javadoc);
217         codeWriter.emitAnnotations(annotations, false);
218         codeWriter.emitModifiers(modifiers, Util.union(implicitModifiers, kind.asMemberModifiers));
219         if (kind == Kind.ANNOTATION) {
220           codeWriter.emit("$L $L", "@interface", name);
221         } else {
222           codeWriter.emit("$L $L", kind.name().toLowerCase(Locale.US), name);
223         }
224         codeWriter.emitTypeVariables(typeVariables);
225 
226         List<TypeName> extendsTypes;
227         List<TypeName> implementsTypes;
228         if (kind == Kind.INTERFACE) {
229           extendsTypes = superinterfaces;
230           implementsTypes = Collections.emptyList();
231         } else {
232           extendsTypes = superclass.equals(ClassName.OBJECT)
233               ? Collections.emptyList()
234               : Collections.singletonList(superclass);
235           implementsTypes = superinterfaces;
236         }
237 
238         if (!extendsTypes.isEmpty()) {
239           codeWriter.emit(" extends");
240           boolean firstType = true;
241           for (TypeName type : extendsTypes) {
242             if (!firstType) codeWriter.emit(",");
243             codeWriter.emit(" $T", type);
244             firstType = false;
245           }
246         }
247 
248         if (!implementsTypes.isEmpty()) {
249           codeWriter.emit(" implements");
250           boolean firstType = true;
251           for (TypeName type : implementsTypes) {
252             if (!firstType) codeWriter.emit(",");
253             codeWriter.emit(" $T", type);
254             firstType = false;
255           }
256         }
257 
258         codeWriter.popType();
259 
260         codeWriter.emit(" {\n");
261       }
262 
263       codeWriter.pushType(this);
264       codeWriter.indent();
265       boolean firstMember = true;
266       for (Iterator<Map.Entry<String, TypeSpec>> i = enumConstants.entrySet().iterator();
267           i.hasNext(); ) {
268         Map.Entry<String, TypeSpec> enumConstant = i.next();
269         if (!firstMember) codeWriter.emit("\n");
270         enumConstant.getValue().emit(codeWriter, enumConstant.getKey(), Collections.emptySet());
271         firstMember = false;
272         if (i.hasNext()) {
273           codeWriter.emit(",\n");
274         } else if (!fieldSpecs.isEmpty() || !methodSpecs.isEmpty() || !typeSpecs.isEmpty()) {
275           codeWriter.emit(";\n");
276         } else {
277           codeWriter.emit("\n");
278         }
279       }
280 
281       // Static fields.
282       for (FieldSpec fieldSpec : fieldSpecs) {
283         if (!fieldSpec.hasModifier(Modifier.STATIC)) continue;
284         if (!firstMember) codeWriter.emit("\n");
285         fieldSpec.emit(codeWriter, kind.implicitFieldModifiers);
286         firstMember = false;
287       }
288 
289       if (!staticBlock.isEmpty()) {
290         if (!firstMember) codeWriter.emit("\n");
291         codeWriter.emit(staticBlock);
292         firstMember = false;
293       }
294 
295       // Non-static fields.
296       for (FieldSpec fieldSpec : fieldSpecs) {
297         if (fieldSpec.hasModifier(Modifier.STATIC)) continue;
298         if (!firstMember) codeWriter.emit("\n");
299         fieldSpec.emit(codeWriter, kind.implicitFieldModifiers);
300         firstMember = false;
301       }
302 
303       // Initializer block.
304       if (!initializerBlock.isEmpty()) {
305         if (!firstMember) codeWriter.emit("\n");
306         codeWriter.emit(initializerBlock);
307         firstMember = false;
308       }
309 
310       // Constructors.
311       for (MethodSpec methodSpec : methodSpecs) {
312         if (!methodSpec.isConstructor()) continue;
313         if (!firstMember) codeWriter.emit("\n");
314         methodSpec.emit(codeWriter, name, kind.implicitMethodModifiers);
315         firstMember = false;
316       }
317 
318       // Methods (static and non-static).
319       for (MethodSpec methodSpec : methodSpecs) {
320         if (methodSpec.isConstructor()) continue;
321         if (!firstMember) codeWriter.emit("\n");
322         methodSpec.emit(codeWriter, name, kind.implicitMethodModifiers);
323         firstMember = false;
324       }
325 
326       // Types.
327       for (TypeSpec typeSpec : typeSpecs) {
328         if (!firstMember) codeWriter.emit("\n");
329         typeSpec.emit(codeWriter, null, kind.implicitTypeModifiers);
330         firstMember = false;
331       }
332 
333       codeWriter.unindent();
334       codeWriter.popType();
335       codeWriter.popTypeVariables(typeVariables);
336 
337       codeWriter.emit("}");
338       if (enumName == null && anonymousTypeArguments == null) {
339         codeWriter.emit("\n"); // If this type isn't also a value, include a trailing newline.
340       }
341     } finally {
342       codeWriter.statementLine = previousStatementLine;
343     }
344   }
345 
equals(Object o)346   @Override public boolean equals(Object o) {
347     if (this == o) return true;
348     if (o == null) return false;
349     if (getClass() != o.getClass()) return false;
350     return toString().equals(o.toString());
351   }
352 
hashCode()353   @Override public int hashCode() {
354     return toString().hashCode();
355   }
356 
toString()357   @Override public String toString() {
358     StringBuilder out = new StringBuilder();
359     try {
360       CodeWriter codeWriter = new CodeWriter(out);
361       emit(codeWriter, null, Collections.emptySet());
362       return out.toString();
363     } catch (IOException e) {
364       throw new AssertionError();
365     }
366   }
367 
368   public enum Kind {
369     CLASS(
370         Collections.emptySet(),
371         Collections.emptySet(),
372         Collections.emptySet(),
373         Collections.emptySet()),
374 
375     INTERFACE(
376         Util.immutableSet(Arrays.asList(Modifier.PUBLIC, Modifier.STATIC, Modifier.FINAL)),
377         Util.immutableSet(Arrays.asList(Modifier.PUBLIC, Modifier.ABSTRACT)),
378         Util.immutableSet(Arrays.asList(Modifier.PUBLIC, Modifier.STATIC)),
379         Util.immutableSet(Collections.singletonList(Modifier.STATIC))),
380 
381     ENUM(
382         Collections.emptySet(),
383         Collections.emptySet(),
384         Collections.emptySet(),
385         Collections.singleton(Modifier.STATIC)),
386 
387     ANNOTATION(
388         Util.immutableSet(Arrays.asList(Modifier.PUBLIC, Modifier.STATIC, Modifier.FINAL)),
389         Util.immutableSet(Arrays.asList(Modifier.PUBLIC, Modifier.ABSTRACT)),
390         Util.immutableSet(Arrays.asList(Modifier.PUBLIC, Modifier.STATIC)),
391         Util.immutableSet(Collections.singletonList(Modifier.STATIC)));
392 
393     private final Set<Modifier> implicitFieldModifiers;
394     private final Set<Modifier> implicitMethodModifiers;
395     private final Set<Modifier> implicitTypeModifiers;
396     private final Set<Modifier> asMemberModifiers;
397 
Kind(Set<Modifier> implicitFieldModifiers, Set<Modifier> implicitMethodModifiers, Set<Modifier> implicitTypeModifiers, Set<Modifier> asMemberModifiers)398     Kind(Set<Modifier> implicitFieldModifiers,
399         Set<Modifier> implicitMethodModifiers,
400         Set<Modifier> implicitTypeModifiers,
401         Set<Modifier> asMemberModifiers) {
402       this.implicitFieldModifiers = implicitFieldModifiers;
403       this.implicitMethodModifiers = implicitMethodModifiers;
404       this.implicitTypeModifiers = implicitTypeModifiers;
405       this.asMemberModifiers = asMemberModifiers;
406     }
407   }
408 
409   public static final class Builder {
410     private final Kind kind;
411     private final String name;
412     private final CodeBlock anonymousTypeArguments;
413 
414     private final CodeBlock.Builder javadoc = CodeBlock.builder();
415     private TypeName superclass = ClassName.OBJECT;
416     private final CodeBlock.Builder staticBlock = CodeBlock.builder();
417     private final CodeBlock.Builder initializerBlock = CodeBlock.builder();
418 
419     public final Map<String, TypeSpec> enumConstants = new LinkedHashMap<>();
420     public final List<AnnotationSpec> annotations = new ArrayList<>();
421     public final List<Modifier> modifiers = new ArrayList<>();
422     public final List<TypeVariableName> typeVariables = new ArrayList<>();
423     public final List<TypeName> superinterfaces = new ArrayList<>();
424     public final List<FieldSpec> fieldSpecs = new ArrayList<>();
425     public final List<MethodSpec> methodSpecs = new ArrayList<>();
426     public final List<TypeSpec> typeSpecs = new ArrayList<>();
427     public final List<Element> originatingElements = new ArrayList<>();
428     public final Set<String> alwaysQualifiedNames = new LinkedHashSet<>();
429 
Builder(Kind kind, String name, CodeBlock anonymousTypeArguments)430     private Builder(Kind kind, String name,
431         CodeBlock anonymousTypeArguments) {
432       checkArgument(name == null || SourceVersion.isName(name), "not a valid name: %s", name);
433       this.kind = kind;
434       this.name = name;
435       this.anonymousTypeArguments = anonymousTypeArguments;
436     }
437 
addJavadoc(String format, Object... args)438     public Builder addJavadoc(String format, Object... args) {
439       javadoc.add(format, args);
440       return this;
441     }
442 
addJavadoc(CodeBlock block)443     public Builder addJavadoc(CodeBlock block) {
444       javadoc.add(block);
445       return this;
446     }
447 
addAnnotations(Iterable<AnnotationSpec> annotationSpecs)448     public Builder addAnnotations(Iterable<AnnotationSpec> annotationSpecs) {
449       checkArgument(annotationSpecs != null, "annotationSpecs == null");
450       for (AnnotationSpec annotationSpec : annotationSpecs) {
451         this.annotations.add(annotationSpec);
452       }
453       return this;
454     }
455 
addAnnotation(AnnotationSpec annotationSpec)456     public Builder addAnnotation(AnnotationSpec annotationSpec) {
457       checkNotNull(annotationSpec, "annotationSpec == null");
458       this.annotations.add(annotationSpec);
459       return this;
460     }
461 
addAnnotation(ClassName annotation)462     public Builder addAnnotation(ClassName annotation) {
463       return addAnnotation(AnnotationSpec.builder(annotation).build());
464     }
465 
addAnnotation(Class<?> annotation)466     public Builder addAnnotation(Class<?> annotation) {
467       return addAnnotation(ClassName.get(annotation));
468     }
469 
addModifiers(Modifier... modifiers)470     public Builder addModifiers(Modifier... modifiers) {
471       Collections.addAll(this.modifiers, modifiers);
472       return this;
473     }
474 
addTypeVariables(Iterable<TypeVariableName> typeVariables)475     public Builder addTypeVariables(Iterable<TypeVariableName> typeVariables) {
476       checkArgument(typeVariables != null, "typeVariables == null");
477       for (TypeVariableName typeVariable : typeVariables) {
478         this.typeVariables.add(typeVariable);
479       }
480       return this;
481     }
482 
addTypeVariable(TypeVariableName typeVariable)483     public Builder addTypeVariable(TypeVariableName typeVariable) {
484       typeVariables.add(typeVariable);
485       return this;
486     }
487 
superclass(TypeName superclass)488     public Builder superclass(TypeName superclass) {
489       checkState(this.kind == Kind.CLASS, "only classes have super classes, not " + this.kind);
490       checkState(this.superclass == ClassName.OBJECT,
491           "superclass already set to " + this.superclass);
492       checkArgument(!superclass.isPrimitive(), "superclass may not be a primitive");
493       this.superclass = superclass;
494       return this;
495     }
496 
superclass(Type superclass)497     public Builder superclass(Type superclass) {
498       return superclass(superclass, true);
499     }
500 
superclass(Type superclass, boolean avoidNestedTypeNameClashes)501     public Builder superclass(Type superclass, boolean avoidNestedTypeNameClashes) {
502       superclass(TypeName.get(superclass));
503       if (avoidNestedTypeNameClashes) {
504         Class<?> clazz = getRawType(superclass);
505         if (clazz != null) {
506           avoidClashesWithNestedClasses(clazz);
507         }
508       }
509       return this;
510     }
511 
superclass(TypeMirror superclass)512     public Builder superclass(TypeMirror superclass) {
513       return superclass(superclass, true);
514     }
515 
superclass(TypeMirror superclass, boolean avoidNestedTypeNameClashes)516     public Builder superclass(TypeMirror superclass, boolean avoidNestedTypeNameClashes) {
517       superclass(TypeName.get(superclass));
518       if (avoidNestedTypeNameClashes && superclass instanceof DeclaredType) {
519         TypeElement superInterfaceElement =
520             (TypeElement) ((DeclaredType) superclass).asElement();
521         avoidClashesWithNestedClasses(superInterfaceElement);
522       }
523       return this;
524     }
525 
addSuperinterfaces(Iterable<? extends TypeName> superinterfaces)526     public Builder addSuperinterfaces(Iterable<? extends TypeName> superinterfaces) {
527       checkArgument(superinterfaces != null, "superinterfaces == null");
528       for (TypeName superinterface : superinterfaces) {
529         addSuperinterface(superinterface);
530       }
531       return this;
532     }
533 
addSuperinterface(TypeName superinterface)534     public Builder addSuperinterface(TypeName superinterface) {
535       checkArgument(superinterface != null, "superinterface == null");
536       this.superinterfaces.add(superinterface);
537       return this;
538     }
539 
addSuperinterface(Type superinterface)540     public Builder addSuperinterface(Type superinterface) {
541       return addSuperinterface(superinterface, true);
542     }
543 
addSuperinterface(Type superinterface, boolean avoidNestedTypeNameClashes)544     public Builder addSuperinterface(Type superinterface, boolean avoidNestedTypeNameClashes) {
545       addSuperinterface(TypeName.get(superinterface));
546       if (avoidNestedTypeNameClashes) {
547         Class<?> clazz = getRawType(superinterface);
548         if (clazz != null) {
549           avoidClashesWithNestedClasses(clazz);
550         }
551       }
552       return this;
553     }
554 
getRawType(Type type)555     private Class<?> getRawType(Type type) {
556       if (type instanceof Class<?>) {
557         return (Class<?>) type;
558       } else if (type instanceof ParameterizedType) {
559         return getRawType(((ParameterizedType) type).getRawType());
560       } else {
561         return null;
562       }
563     }
564 
addSuperinterface(TypeMirror superinterface)565     public Builder addSuperinterface(TypeMirror superinterface) {
566       return addSuperinterface(superinterface, true);
567     }
568 
addSuperinterface(TypeMirror superinterface, boolean avoidNestedTypeNameClashes)569     public Builder addSuperinterface(TypeMirror superinterface,
570         boolean avoidNestedTypeNameClashes) {
571       addSuperinterface(TypeName.get(superinterface));
572       if (avoidNestedTypeNameClashes && superinterface instanceof DeclaredType) {
573         TypeElement superInterfaceElement =
574             (TypeElement) ((DeclaredType) superinterface).asElement();
575         avoidClashesWithNestedClasses(superInterfaceElement);
576       }
577       return this;
578     }
579 
addEnumConstant(String name)580     public Builder addEnumConstant(String name) {
581       return addEnumConstant(name, anonymousClassBuilder("").build());
582     }
583 
addEnumConstant(String name, TypeSpec typeSpec)584     public Builder addEnumConstant(String name, TypeSpec typeSpec) {
585       enumConstants.put(name, typeSpec);
586       return this;
587     }
588 
addFields(Iterable<FieldSpec> fieldSpecs)589     public Builder addFields(Iterable<FieldSpec> fieldSpecs) {
590       checkArgument(fieldSpecs != null, "fieldSpecs == null");
591       for (FieldSpec fieldSpec : fieldSpecs) {
592         addField(fieldSpec);
593       }
594       return this;
595     }
596 
addField(FieldSpec fieldSpec)597     public Builder addField(FieldSpec fieldSpec) {
598       fieldSpecs.add(fieldSpec);
599       return this;
600     }
601 
addField(TypeName type, String name, Modifier... modifiers)602     public Builder addField(TypeName type, String name, Modifier... modifiers) {
603       return addField(FieldSpec.builder(type, name, modifiers).build());
604     }
605 
addField(Type type, String name, Modifier... modifiers)606     public Builder addField(Type type, String name, Modifier... modifiers) {
607       return addField(TypeName.get(type), name, modifiers);
608     }
609 
addStaticBlock(CodeBlock block)610     public Builder addStaticBlock(CodeBlock block) {
611       staticBlock.beginControlFlow("static").add(block).endControlFlow();
612       return this;
613     }
614 
addInitializerBlock(CodeBlock block)615     public Builder addInitializerBlock(CodeBlock block) {
616       if ((kind != Kind.CLASS && kind != Kind.ENUM)) {
617         throw new UnsupportedOperationException(kind + " can't have initializer blocks");
618       }
619       initializerBlock.add("{\n")
620           .indent()
621           .add(block)
622           .unindent()
623           .add("}\n");
624       return this;
625     }
626 
addMethods(Iterable<MethodSpec> methodSpecs)627     public Builder addMethods(Iterable<MethodSpec> methodSpecs) {
628       checkArgument(methodSpecs != null, "methodSpecs == null");
629       for (MethodSpec methodSpec : methodSpecs) {
630         addMethod(methodSpec);
631       }
632       return this;
633     }
634 
addMethod(MethodSpec methodSpec)635     public Builder addMethod(MethodSpec methodSpec) {
636       methodSpecs.add(methodSpec);
637       return this;
638     }
639 
addTypes(Iterable<TypeSpec> typeSpecs)640     public Builder addTypes(Iterable<TypeSpec> typeSpecs) {
641       checkArgument(typeSpecs != null, "typeSpecs == null");
642       for (TypeSpec typeSpec : typeSpecs) {
643         addType(typeSpec);
644       }
645       return this;
646     }
647 
addType(TypeSpec typeSpec)648     public Builder addType(TypeSpec typeSpec) {
649       typeSpecs.add(typeSpec);
650       return this;
651     }
652 
addOriginatingElement(Element originatingElement)653     public Builder addOriginatingElement(Element originatingElement) {
654       originatingElements.add(originatingElement);
655       return this;
656     }
657 
alwaysQualify(String... simpleNames)658     public Builder alwaysQualify(String... simpleNames) {
659       checkArgument(simpleNames != null, "simpleNames == null");
660       for (String name : simpleNames) {
661         checkArgument(
662             name != null,
663             "null entry in simpleNames array: %s",
664             Arrays.toString(simpleNames)
665         );
666         alwaysQualifiedNames.add(name);
667       }
668       return this;
669     }
670 
671     /**
672      * Call this to always fully qualify any types that would conflict with possibly nested types of
673      * this {@code typeElement}. For example - if the following type was passed in as the
674      * typeElement:
675      *
676      * <pre><code>
677      *   class Foo {
678      *     class NestedTypeA {
679      *
680      *     }
681      *     class NestedTypeB {
682      *
683      *     }
684      *   }
685      * </code></pre>
686      *
687      * <p>
688      * Then this would add {@code "NestedTypeA"} and {@code "NestedTypeB"} as names that should
689      * always be qualified via {@link #alwaysQualify(String...)}. This way they would avoid
690      * possible import conflicts when this JavaFile is written.
691      *
692      * @param typeElement the {@link TypeElement} with nested types to avoid clashes with.
693      * @return this builder instance.
694      */
avoidClashesWithNestedClasses(TypeElement typeElement)695     public Builder avoidClashesWithNestedClasses(TypeElement typeElement) {
696       checkArgument(typeElement != null, "typeElement == null");
697       for (TypeElement nestedType : ElementFilter.typesIn(typeElement.getEnclosedElements())) {
698         alwaysQualify(nestedType.getSimpleName().toString());
699       }
700       TypeMirror superclass = typeElement.getSuperclass();
701       if (!(superclass instanceof NoType) && superclass instanceof DeclaredType) {
702         TypeElement superclassElement = (TypeElement) ((DeclaredType) superclass).asElement();
703         avoidClashesWithNestedClasses(superclassElement);
704       }
705       for (TypeMirror superinterface : typeElement.getInterfaces()) {
706         if (superinterface instanceof DeclaredType) {
707           TypeElement superinterfaceElement
708               = (TypeElement) ((DeclaredType) superinterface).asElement();
709           avoidClashesWithNestedClasses(superinterfaceElement);
710         }
711       }
712       return this;
713     }
714 
715     /**
716      * Call this to always fully qualify any types that would conflict with possibly nested types of
717      * this {@code typeElement}. For example - if the following type was passed in as the
718      * typeElement:
719      *
720      * <pre><code>
721      *   class Foo {
722      *     class NestedTypeA {
723      *
724      *     }
725      *     class NestedTypeB {
726      *
727      *     }
728      *   }
729      * </code></pre>
730      *
731      * <p>
732      * Then this would add {@code "NestedTypeA"} and {@code "NestedTypeB"} as names that should
733      * always be qualified via {@link #alwaysQualify(String...)}. This way they would avoid
734      * possible import conflicts when this JavaFile is written.
735      *
736      * @param clazz the {@link Class} with nested types to avoid clashes with.
737      * @return this builder instance.
738      */
avoidClashesWithNestedClasses(Class<?> clazz)739     public Builder avoidClashesWithNestedClasses(Class<?> clazz) {
740       checkArgument(clazz != null, "clazz == null");
741       for (Class<?> nestedType : clazz.getDeclaredClasses()) {
742         alwaysQualify(nestedType.getSimpleName());
743       }
744       Class<?> superclass = clazz.getSuperclass();
745       if (superclass != null && !Object.class.equals(superclass)) {
746         avoidClashesWithNestedClasses(superclass);
747       }
748       for (Class<?> superinterface : clazz.getInterfaces()) {
749         avoidClashesWithNestedClasses(superinterface);
750       }
751       return this;
752     }
753 
build()754     public TypeSpec build() {
755       for (AnnotationSpec annotationSpec : annotations) {
756         checkNotNull(annotationSpec, "annotationSpec == null");
757       }
758 
759       if (!modifiers.isEmpty()) {
760         checkState(anonymousTypeArguments == null, "forbidden on anonymous types.");
761         for (Modifier modifier : modifiers) {
762           checkArgument(modifier != null, "modifiers contain null");
763         }
764       }
765 
766       checkArgument(kind != Kind.ENUM || !enumConstants.isEmpty(),
767           "at least one enum constant is required for %s", name);
768 
769       for (TypeName superinterface : superinterfaces) {
770         checkArgument(superinterface != null, "superinterfaces contains null");
771       }
772 
773       if (!typeVariables.isEmpty()) {
774         checkState(anonymousTypeArguments == null,
775             "typevariables are forbidden on anonymous types.");
776         for (TypeVariableName typeVariableName : typeVariables) {
777           checkArgument(typeVariableName != null, "typeVariables contain null");
778         }
779       }
780 
781       for (Map.Entry<String, TypeSpec> enumConstant : enumConstants.entrySet()) {
782         checkState(kind == Kind.ENUM, "%s is not enum", this.name);
783         checkArgument(enumConstant.getValue().anonymousTypeArguments != null,
784             "enum constants must have anonymous type arguments");
785         checkArgument(SourceVersion.isName(name), "not a valid enum constant: %s", name);
786       }
787 
788       for (FieldSpec fieldSpec : fieldSpecs) {
789         if (kind == Kind.INTERFACE || kind == Kind.ANNOTATION) {
790           requireExactlyOneOf(fieldSpec.modifiers, Modifier.PUBLIC, Modifier.PRIVATE);
791           Set<Modifier> check = EnumSet.of(Modifier.STATIC, Modifier.FINAL);
792           checkState(fieldSpec.modifiers.containsAll(check), "%s %s.%s requires modifiers %s",
793               kind, name, fieldSpec.name, check);
794         }
795       }
796 
797       for (MethodSpec methodSpec : methodSpecs) {
798         if (kind == Kind.INTERFACE) {
799           requireExactlyOneOf(methodSpec.modifiers, Modifier.ABSTRACT, Modifier.STATIC,
800               Modifier.DEFAULT);
801           requireExactlyOneOf(methodSpec.modifiers, Modifier.PUBLIC, Modifier.PRIVATE);
802         } else if (kind == Kind.ANNOTATION) {
803           checkState(methodSpec.modifiers.equals(kind.implicitMethodModifiers),
804               "%s %s.%s requires modifiers %s",
805               kind, name, methodSpec.name, kind.implicitMethodModifiers);
806         }
807         if (kind != Kind.ANNOTATION) {
808           checkState(methodSpec.defaultValue == null, "%s %s.%s cannot have a default value",
809               kind, name, methodSpec.name);
810         }
811         if (kind != Kind.INTERFACE) {
812           checkState(!methodSpec.hasModifier(Modifier.DEFAULT), "%s %s.%s cannot be default",
813               kind, name, methodSpec.name);
814         }
815       }
816 
817       for (TypeSpec typeSpec : typeSpecs) {
818         checkArgument(typeSpec.modifiers.containsAll(kind.implicitTypeModifiers),
819             "%s %s.%s requires modifiers %s", kind, name, typeSpec.name,
820             kind.implicitTypeModifiers);
821       }
822 
823       boolean isAbstract = modifiers.contains(Modifier.ABSTRACT) || kind != Kind.CLASS;
824       for (MethodSpec methodSpec : methodSpecs) {
825         checkArgument(isAbstract || !methodSpec.hasModifier(Modifier.ABSTRACT),
826             "non-abstract type %s cannot declare abstract method %s", name, methodSpec.name);
827       }
828 
829       boolean superclassIsObject = superclass.equals(ClassName.OBJECT);
830       int interestingSupertypeCount = (superclassIsObject ? 0 : 1) + superinterfaces.size();
831       checkArgument(anonymousTypeArguments == null || interestingSupertypeCount <= 1,
832           "anonymous type has too many supertypes");
833 
834       return new TypeSpec(this);
835     }
836   }
837 }
838