Codebase list javapoet / 2cafca0
New upstream version 1.12.1 tony mancill 3 years ago
29 changed file(s) with 1563 addition(s) and 142 deletion(s). Raw diff Collapse all Expand all
55 # https://benlimmer.com/2013/12/26/automatically-publish-javadoc-to-gh-pages-with-travis-ci/
66
77 SLUG="square/javapoet"
8 JDK="oraclejdk8"
8 JDK="openjdk8"
99 BRANCH="master"
1010
1111 set -e
11
22 matrix:
33 include:
4 - env: JDK='Oracle JDK 8'
5 jdk: oraclejdk8
6 - env: JDK='Oracle JDK 9'
7 jdk: oraclejdk9
8 - env: JDK='Oracle JDK 10'
9 install: . ./install-jdk.sh -F 10 -L BCL
10 - env: JDK='OpenJDK 10'
11 install: . ./install-jdk.sh -F 10 -L GPL
12 - env: JDK='Oracle JDK 11'
13 install: . ./install-jdk.sh -F 11 -L BCL
14 - env: JDK='OpenJDK 11'
15 install: . ./install-jdk.sh -F 11 -L GPL
4 - jdk: openjdk8
5 - jdk: openjdk11
166 allow_failures:
17 # ErrorProne/javac is not yet working on JDK 11
18 - env: JDK='Oracle JDK 11'
19 - env: JDK='OpenJDK 11'
7 - jdk: openjdk11
208
21 # Direct usage of `install-jdk.sh` might be superseded by https://github.com/travis-ci/travis-build/pull/1347
229 before_install:
2310 - unset _JAVA_OPTIONS
24 - wget https://github.com/sormuras/bach/raw/1.0.1/install-jdk.sh
2511
2612 after_success:
2713 - .buildscript/deploy_snapshot.sh
3824 notifications:
3925 email: false
4026
41 sudo: false
42
4327 cache:
4428 directories:
4529 - $HOME/.m2
00 Change Log
11 ==========
2
3 JavaPoet 1.12.0 *(2020-01-09)*
4 -----------------------------
5
6 * New: Add `JavaFile.writeToPath()` and `JavaFile.writeToFile()` methods that return paths to the
7 generated file as `Path` and `File` respectively.
8 * New: Add `TypeSpec.alwaysQualify()` API to avoid clashes involving nested type names.
9 * New: Add overloads accepting `CodeBlock`s to `MethodSpec`'s control flow methods.
10 * New: Make list fields of all `Builder` types mutable.
11 * New: Add `CodeBlock.clear()`.
12 * New: Allow passing a custom `Charset` to `JavaFile.writeTo()`.
13 * New: Improved performance of `ClassName.simpleNames()` by memoizing results.
14 * New: Significant performance improvements for `CodeWriter.resolve()` as all nested simple names
15 of a `TypeSpec` get pre-computed.
16 * New: Add `TypeName.Builder.setName()` to allow overriding names passed in the constructor.
17 * New: Add `TypeName.canonicalName()`.
18 * Fix: Use `\\R` instead of `\n` as line separator in `CodeWriter.emitAndIndent()`.
19 * Fix: Copy originating elements in `TypeSpec.toBuilder()`.
20 * Fix: Ensure trailing newlines in Javadocs and method bodies.
21 * Fix: Copy annotations when creating a `ParameterSpec` from a `VariableElement`.
22 * Fix: Properly handle classes located in empty packages in `ClassName`.
23 * Fix: Only allow `final` modifier on a `ParameterSpec`.
24 * Fix: Use fully-qualified names for type names that are masked by type variable names.
25
226
327 JavaPoet 1.11.1 *(2018-05-16)*
428 -----------------------------
127127 Methods generating methods! And since JavaPoet generates source instead of bytecode, you can
128128 read through it to make sure it's right.
129129
130 Some control flow statements, such as `if/else`, can have unlimited control flow possibilities.
131 You can handle those options using `nextControlFlow()`:
132
133 ```java
134 MethodSpec main = MethodSpec.methodBuilder("main")
135 .addStatement("long now = $T.currentTimeMillis()", System.class)
136 .beginControlFlow("if ($T.currentTimeMillis() < now)", System.class)
137 .addStatement("$T.out.println($S)", System.class, "Time travelling, woo hoo!")
138 .nextControlFlow("else if ($T.currentTimeMillis() == now)", System.class)
139 .addStatement("$T.out.println($S)", System.class, "Time stood still!")
140 .nextControlFlow("else")
141 .addStatement("$T.out.println($S)", System.class, "Ok, time still moving forward")
142 .endControlFlow()
143 .build();
144 ```
145
146 Which generates:
147
148 ```java
149 void main() {
150 long now = System.currentTimeMillis();
151 if (System.currentTimeMillis() < now) {
152 System.out.println("Time travelling, woo hoo!");
153 } else if (System.currentTimeMillis() == now) {
154 System.out.println("Time stood still!");
155 } else {
156 System.out.println("Ok, time still moving forward");
157 }
158 }
159 ```
160
161 Catching exceptions using `try/catch` is also a use case for `nextControlFlow()`:
162
163 ```java
164 MethodSpec main = MethodSpec.methodBuilder("main")
165 .beginControlFlow("try")
166 .addStatement("throw new Exception($S)", "Failed")
167 .nextControlFlow("catch ($T e)", Exception.class)
168 .addStatement("throw new $T(e)", RuntimeException.class)
169 .endControlFlow()
170 .build();
171 ```
172
173 Which produces:
174
175 ```java
176 void main() {
177 try {
178 throw new Exception("Failed");
179 } catch (Exception e) {
180 throw new RuntimeException(e);
181 }
182 }
183 ```
130184
131185 ### $L for Literals
132186
673727
674728 ### Anonymous Inner Classes
675729
676 In the enum code, we used `Types.anonymousInnerClass()`. Anonymous inner classes can also be used in
730 In the enum code, we used `TypeSpec.anonymousInnerClass()`. Anonymous inner classes can also be used in
677731 code blocks. They are values that can be referenced with `$L`:
678732
679733 ```java
837891 <dependency>
838892 <groupId>com.squareup</groupId>
839893 <artifactId>javapoet</artifactId>
840 <version>1.11.1</version>
894 <version>1.12.0</version>
841895 </dependency>
842896 ```
843897 or Gradle:
844898 ```groovy
845 compile 'com.squareup:javapoet:1.11.1'
899 compile 'com.squareup:javapoet:1.12.0'
846900 ```
847901
848902 Snapshots of the development version are available in [Sonatype's `snapshots` repository][snap].
1010
1111 <groupId>com.squareup</groupId>
1212 <artifactId>javapoet</artifactId>
13 <version>1.11.1</version>
13 <version>1.12.1</version>
1414
1515 <name>JavaPoet</name>
1616 <description>Use beautiful Java code to generate beautiful Java code.</description>
9393 <plugin>
9494 <groupId>org.apache.maven.plugins</groupId>
9595 <artifactId>maven-compiler-plugin</artifactId>
96 <version>3.7.0</version>
96 <version>3.8.0</version>
9797 <configuration>
9898 <compilerId>javac-with-errorprone</compilerId>
9999 <forceJavacCompilerUse>true</forceJavacCompilerUse>
122122 <dependency>
123123 <groupId>com.puppycrawl.tools</groupId>
124124 <artifactId>checkstyle</artifactId>
125 <version>8.7</version>
125 <version>8.18</version>
126126 </dependency>
127127 </dependencies>
128128 <configuration>
191191
192192 public static final class Builder {
193193 private final TypeName type;
194 private final Map<String, List<CodeBlock>> members = new LinkedHashMap<>();
194
195 public final Map<String, List<CodeBlock>> members = new LinkedHashMap<>();
195196
196197 private Builder(TypeName type) {
197198 this.type = type;
202203 }
203204
204205 public Builder addMember(String name, CodeBlock codeBlock) {
205 checkNotNull(name, "name == null");
206 checkArgument(SourceVersion.isName(name), "not a valid name: %s", name);
207206 List<CodeBlock> values = members.computeIfAbsent(name, k -> new ArrayList<>());
208207 values.add(codeBlock);
209208 return this;
237236 }
238237
239238 public AnnotationSpec build() {
239 for (String name : members.keySet()) {
240 checkNotNull(name, "name == null");
241 checkArgument(SourceVersion.isName(name), "not a valid name: %s", name);
242 }
240243 return new AnnotationSpec(this);
241244 }
242245 }
1919 import java.util.Collections;
2020 import java.util.List;
2121 import java.util.Map;
22 import java.util.Objects;
2223 import javax.lang.model.element.Element;
2324 import javax.lang.model.element.PackageElement;
2425 import javax.lang.model.element.TypeElement;
3132 public final class ClassName extends TypeName implements Comparable<ClassName> {
3233 public static final ClassName OBJECT = ClassName.get(Object.class);
3334
35 /** The name representing the default Java package. */
36 private static final String NO_PACKAGE = "";
37
3438 /** The package name of this class, or "" if this is in the default package. */
3539 final String packageName;
3640
3943
4044 /** This class name, like "Entry" for java.util.Map.Entry. */
4145 final String simpleName;
46
47 private List<String> simpleNames;
4248
4349 /** The full class name like "java.util.Map.Entry". */
4450 final String canonicalName;
5056 private ClassName(String packageName, ClassName enclosingClassName, String simpleName,
5157 List<AnnotationSpec> annotations) {
5258 super(annotations);
53 this.packageName = packageName;
59 this.packageName = Objects.requireNonNull(packageName, "packageName == null");
5460 this.enclosingClassName = enclosingClassName;
5561 this.simpleName = simpleName;
5662 this.canonicalName = enclosingClassName != null
107113 }
108114
109115 public List<String> simpleNames() {
110 List<String> simpleNames = new ArrayList<>();
111 if (enclosingClassName != null) {
112 simpleNames.addAll(enclosingClassName().simpleNames());
113 }
114 simpleNames.add(simpleName);
116 if (simpleNames != null) {
117 return simpleNames;
118 }
119
120 if (enclosingClassName == null) {
121 simpleNames = Collections.singletonList(simpleName);
122 } else {
123 List<String> mutableNames = new ArrayList<>();
124 mutableNames.addAll(enclosingClassName().simpleNames());
125 mutableNames.add(simpleName);
126 simpleNames = Collections.unmodifiableList(mutableNames);
127 }
115128 return simpleNames;
116129 }
117130
135148 /** Returns the simple name of this class, like {@code "Entry"} for {@link Map.Entry}. */
136149 public String simpleName() {
137150 return simpleName;
151 }
152
153 /**
154 * Returns the full class name of this class.
155 * Like {@code "java.util.Map.Entry"} for {@link Map.Entry}.
156 * */
157 public String canonicalName() {
158 return canonicalName;
138159 }
139160
140161 public static ClassName get(Class<?> clazz) {
154175 if (clazz.getEnclosingClass() == null) {
155176 // Avoid unreliable Class.getPackage(). https://github.com/square/javapoet/issues/295
156177 int lastDot = clazz.getName().lastIndexOf('.');
157 String packageName = (lastDot != -1) ? clazz.getName().substring(0, lastDot) : null;
178 String packageName = (lastDot != -1) ? clazz.getName().substring(0, lastDot) : NO_PACKAGE;
158179 return new ClassName(packageName, null, name);
159180 }
160181
176197 p = classNameString.indexOf('.', p) + 1;
177198 checkArgument(p != 0, "couldn't make a guess for %s", classNameString);
178199 }
179 String packageName = p == 0 ? "" : classNameString.substring(0, p - 1);
200 String packageName = p == 0 ? NO_PACKAGE : classNameString.substring(0, p - 1);
180201
181202 // Add class names like "Map" and "Entry".
182203 ClassName className = null;
188188 while (p < format.length()) {
189189 int nextP = format.indexOf("$", p);
190190 if (nextP == -1) {
191 formatParts.add(format.substring(p, format.length()));
191 formatParts.add(format.substring(p));
192192 break;
193193 }
194194
423423 return this;
424424 }
425425
426 public Builder clear() {
427 formatParts.clear();
428 args.clear();
429 return this;
430 }
431
426432 public CodeBlock build() {
427433 return new CodeBlock(this);
428434 }
5353 private final List<TypeSpec> typeSpecStack = new ArrayList<>();
5454 private final Set<String> staticImportClassNames;
5555 private final Set<String> staticImports;
56 private final Set<String> alwaysQualify;
5657 private final Map<String, ClassName> importedTypes;
5758 private final Map<String, ClassName> importableTypes = new LinkedHashMap<>();
5859 private final Set<String> referencedNames = new LinkedHashSet<>();
60 private final Multiset<String> currentTypeVariables = new Multiset<>();
5961 private boolean trailingNewline;
6062
6163 /**
6668 int statementLine = -1;
6769
6870 CodeWriter(Appendable out) {
69 this(out, " ", Collections.emptySet());
70 }
71
72 CodeWriter(Appendable out, String indent, Set<String> staticImports) {
73 this(out, indent, Collections.emptyMap(), staticImports);
74 }
75
76 CodeWriter(Appendable out, String indent, Map<String, ClassName> importedTypes,
77 Set<String> staticImports) {
71 this(out, " ", Collections.emptySet(), Collections.emptySet());
72 }
73
74 CodeWriter(Appendable out, String indent, Set<String> staticImports, Set<String> alwaysQualify) {
75 this(out, indent, Collections.emptyMap(), staticImports, alwaysQualify);
76 }
77
78 CodeWriter(Appendable out,
79 String indent,
80 Map<String, ClassName> importedTypes,
81 Set<String> staticImports,
82 Set<String> alwaysQualify) {
7883 this.out = new LineWrapper(out, indent, 100);
7984 this.indent = checkNotNull(indent, "indent == null");
8085 this.importedTypes = checkNotNull(importedTypes, "importedTypes == null");
8186 this.staticImports = checkNotNull(staticImports, "staticImports == null");
87 this.alwaysQualify = checkNotNull(alwaysQualify, "alwaysQualify == null");
8288 this.staticImportClassNames = new LinkedHashSet<>();
8389 for (String signature : staticImports) {
8490 staticImportClassNames.add(signature.substring(0, signature.lastIndexOf('.')));
147153 emit("/**\n");
148154 javadoc = true;
149155 try {
150 emit(javadocCodeBlock);
156 emit(javadocCodeBlock, true);
151157 } finally {
152158 javadoc = false;
153159 }
185191 */
186192 public void emitTypeVariables(List<TypeVariableName> typeVariables) throws IOException {
187193 if (typeVariables.isEmpty()) return;
194
195 typeVariables.forEach(typeVariable -> currentTypeVariables.add(typeVariable.name));
188196
189197 emit("<");
190198 boolean firstTypeVariable = true;
202210 emit(">");
203211 }
204212
213 public void popTypeVariables(List<TypeVariableName> typeVariables) throws IOException {
214 typeVariables.forEach(typeVariable -> currentTypeVariables.remove(typeVariable.name));
215 }
216
205217 public CodeWriter emit(String s) throws IOException {
206218 return emitAndIndent(s);
207219 }
211223 }
212224
213225 public CodeWriter emit(CodeBlock codeBlock) throws IOException {
226 return emit(codeBlock, false);
227 }
228
229 public CodeWriter emit(CodeBlock codeBlock, boolean ensureTrailingNewline) throws IOException {
214230 int a = 0;
215231 ClassName deferredTypeName = null; // used by "import static" logic
216232 ListIterator<String> partIterator = codeBlock.formatParts.listIterator();
299315 break;
300316 }
301317 }
318 if (ensureTrailingNewline && out.lastChar() != '\n') {
319 emit("\n");
320 }
302321 return this;
303322 }
304323
352371 * names visible due to inheritance.
353372 */
354373 String lookupName(ClassName className) {
374 // If the top level simple name is masked by a current type variable, use the canonical name.
375 String topLevelSimpleName = className.topLevelClassName().simpleName();
376 if (currentTypeVariables.contains(topLevelSimpleName)) {
377 return className.canonicalName;
378 }
379
355380 // Find the shortest suffix of className that resolves to className. This uses both local type
356381 // names (so `Entry` in `Map` refers to `Map.Entry`). Also uses imports.
357382 boolean nameResolved = false;
373398
374399 // If the class is in the same package, we're done.
375400 if (Objects.equals(packageName, className.packageName())) {
376 referencedNames.add(className.topLevelClassName().simpleName());
401 referencedNames.add(topLevelSimpleName);
377402 return join(".", className.simpleNames());
378403 }
379404
387412
388413 private void importableType(ClassName className) {
389414 if (className.packageName().isEmpty()) {
415 return;
416 } else if (alwaysQualify.contains(className.simpleName)) {
417 // TODO what about nested types like java.util.Map.Entry?
390418 return;
391419 }
392420 ClassName topLevelClassName = className.topLevelClassName();
406434 // Match a child of the current (potentially nested) class.
407435 for (int i = typeSpecStack.size() - 1; i >= 0; i--) {
408436 TypeSpec typeSpec = typeSpecStack.get(i);
409 for (TypeSpec visibleChild : typeSpec.typeSpecs) {
410 if (Objects.equals(visibleChild.name, simpleName)) {
411 return stackClassName(i, simpleName);
412 }
437 if (typeSpec.nestedTypesSimpleNames.contains(simpleName)) {
438 return stackClassName(i, simpleName);
413439 }
414440 }
415441
442468 */
443469 CodeWriter emitAndIndent(String s) throws IOException {
444470 boolean first = true;
445 for (String line : s.split("\n", -1)) {
471 for (String line : s.split("\\R", -1)) {
446472 // Emit a newline character. Make sure blank lines in Javadoc & comments look good.
447473 if (!first) {
448474 if ((javadoc || comment) && trailingNewline) {
493519 result.keySet().removeAll(referencedNames);
494520 return result;
495521 }
522
523 // A makeshift multi-set implementation
524 private static final class Multiset<T> {
525 private final Map<T, Integer> map = new LinkedHashMap<>();
526
527 void add(T t) {
528 int count = map.getOrDefault(t, 0);
529 map.put(t, count + 1);
530 }
531
532 void remove(T t) {
533 int count = map.getOrDefault(t, 0);
534 if (count == 0) {
535 throw new IllegalStateException(t + " is not in the multiset");
536 }
537 map.put(t, count - 1);
538 }
539
540 boolean contains(T t) {
541 return map.getOrDefault(t, 0) > 0;
542 }
543 }
496544 }
110110 private final String name;
111111
112112 private final CodeBlock.Builder javadoc = CodeBlock.builder();
113 private final List<AnnotationSpec> annotations = new ArrayList<>();
114 private final List<Modifier> modifiers = new ArrayList<>();
115113 private CodeBlock initializer = null;
114
115 public final List<AnnotationSpec> annotations = new ArrayList<>();
116 public final List<Modifier> modifiers = new ArrayList<>();
116117
117118 private Builder(TypeName type, String name) {
118119 this.type = type;
2121 import java.io.OutputStreamWriter;
2222 import java.io.Writer;
2323 import java.net.URI;
24 import java.nio.charset.Charset;
2425 import java.nio.file.Files;
2526 import java.nio.file.Path;
2627 import java.util.Arrays;
2728 import java.util.Collections;
29 import java.util.LinkedHashSet;
2830 import java.util.List;
2931 import java.util.Map;
3032 import java.util.Set;
5860 public final TypeSpec typeSpec;
5961 public final boolean skipJavaLangImports;
6062 private final Set<String> staticImports;
63 private final Set<String> alwaysQualify;
6164 private final String indent;
6265
6366 private JavaFile(Builder builder) {
6770 this.skipJavaLangImports = builder.skipJavaLangImports;
6871 this.staticImports = Util.immutableSet(builder.staticImports);
6972 this.indent = builder.indent;
73
74 Set<String> alwaysQualifiedNames = new LinkedHashSet<>();
75 fillAlwaysQualifiedNames(builder.typeSpec, alwaysQualifiedNames);
76 this.alwaysQualify = Util.immutableSet(alwaysQualifiedNames);
77 }
78
79 private void fillAlwaysQualifiedNames(TypeSpec spec, Set<String> alwaysQualifiedNames) {
80 alwaysQualifiedNames.addAll(spec.alwaysQualifiedNames);
81 for (TypeSpec nested : spec.typeSpecs) {
82 fillAlwaysQualifiedNames(nested, alwaysQualifiedNames);
83 }
7084 }
7185
7286 public void writeTo(Appendable out) throws IOException {
7387 // First pass: emit the entire class, just to collect the types we'll need to import.
74 CodeWriter importsCollector = new CodeWriter(NULL_APPENDABLE, indent, staticImports);
88 CodeWriter importsCollector = new CodeWriter(
89 NULL_APPENDABLE,
90 indent,
91 staticImports,
92 alwaysQualify
93 );
7594 emit(importsCollector);
7695 Map<String, ClassName> suggestedImports = importsCollector.suggestedImports();
7796
7897 // Second pass: write the code, taking advantage of the imports.
79 CodeWriter codeWriter = new CodeWriter(out, indent, suggestedImports, staticImports);
98 CodeWriter codeWriter
99 = new CodeWriter(out, indent, suggestedImports, staticImports, alwaysQualify);
80100 emit(codeWriter);
81101 }
82102
83103 /** Writes this to {@code directory} as UTF-8 using the standard directory structure. */
84104 public void writeTo(Path directory) throws IOException {
105 writeToPath(directory);
106 }
107
108 /**
109 * Writes this to {@code directory} with the provided {@code charset} using the standard directory
110 * structure.
111 */
112 public void writeTo(Path directory, Charset charset) throws IOException {
113 writeToPath(directory, charset);
114 }
115
116 /**
117 * Writes this to {@code directory} as UTF-8 using the standard directory structure.
118 * Returns the {@link Path} instance to which source is actually written.
119 */
120 public Path writeToPath(Path directory) throws IOException {
121 return writeToPath(directory, UTF_8);
122 }
123
124 /**
125 * Writes this to {@code directory} with the provided {@code charset} using the standard directory
126 * structure.
127 * Returns the {@link Path} instance to which source is actually written.
128 */
129 public Path writeToPath(Path directory, Charset charset) throws IOException {
85130 checkArgument(Files.notExists(directory) || Files.isDirectory(directory),
86131 "path %s exists but is not a directory.", directory);
87132 Path outputDirectory = directory;
93138 }
94139
95140 Path outputPath = outputDirectory.resolve(typeSpec.name + ".java");
96 try (Writer writer = new OutputStreamWriter(Files.newOutputStream(outputPath), UTF_8)) {
141 try (Writer writer = new OutputStreamWriter(Files.newOutputStream(outputPath), charset)) {
97142 writeTo(writer);
98143 }
144
145 return outputPath;
99146 }
100147
101148 /** Writes this to {@code directory} as UTF-8 using the standard directory structure. */
102149 public void writeTo(File directory) throws IOException {
103150 writeTo(directory.toPath());
151 }
152
153 /**
154 * Writes this to {@code directory} as UTF-8 using the standard directory structure.
155 * Returns the {@link File} instance to which source is actually written.
156 */
157 public File writeToFile(File directory) throws IOException {
158 final Path outputPath = writeToPath(directory.toPath());
159 return outputPath.toFile();
104160 }
105161
106162 /** Writes this to {@code filer}. */
143199
144200 int importedTypesCount = 0;
145201 for (ClassName className : new TreeSet<>(codeWriter.importedTypes().values())) {
146 if (skipJavaLangImports && className.packageName().equals("java.lang")) continue;
202 // TODO what about nested types like java.util.Map.Entry?
203 if (skipJavaLangImports
204 && className.packageName().equals("java.lang")
205 && !alwaysQualify.contains(className.simpleName)) {
206 continue;
207 }
147208 codeWriter.emit("import $L;\n", className.withoutAnnotations());
148209 importedTypesCount++;
149210 }
215276 private final String packageName;
216277 private final TypeSpec typeSpec;
217278 private final CodeBlock.Builder fileComment = CodeBlock.builder();
218 private final Set<String> staticImports = new TreeSet<>();
219279 private boolean skipJavaLangImports;
220280 private String indent = " ";
281
282 public final Set<String> staticImports = new TreeSet<>();
221283
222284 private Builder(String packageName, TypeSpec typeSpec) {
223285 this.packageName = packageName;
2323 * or soft-wrapping spaces using {@link #wrappingSpace}.
2424 */
2525 final class LineWrapper {
26 private final Appendable out;
26 private final RecordingAppendable out;
2727 private final String indent;
2828 private final int columnLimit;
2929 private boolean closed;
4646
4747 LineWrapper(Appendable out, String indent, int columnLimit) {
4848 checkNotNull(out, "out == null");
49 this.out = out;
49 this.out = new RecordingAppendable(out);
5050 this.indent = indent;
5151 this.columnLimit = columnLimit;
52 }
53
54 /** @return the last emitted char or {@link Character#MIN_VALUE} if nothing emitted yet. */
55 char lastChar() {
56 return out.lastChar;
5257 }
5358
5459 /** Emit {@code s}. This may be buffered to permit line wraps to be inserted. */
133138 private enum FlushType {
134139 WRAP, SPACE, EMPTY;
135140 }
141
142 /** A delegating {@link Appendable} that records info about the chars passing through it. */
143 static final class RecordingAppendable implements Appendable {
144 private final Appendable delegate;
145
146 char lastChar = Character.MIN_VALUE;
147
148 RecordingAppendable(Appendable delegate) {
149 this.delegate = delegate;
150 }
151
152 @Override public Appendable append(CharSequence csq) throws IOException {
153 int length = csq.length();
154 if (length != 0) {
155 lastChar = csq.charAt(length - 1);
156 }
157 return delegate.append(csq);
158 }
159
160 @Override public Appendable append(CharSequence csq, int start, int end) throws IOException {
161 CharSequence sub = csq.subSequence(start, end);
162 return append(sub);
163 }
164
165 @Override public Appendable append(char c) throws IOException {
166 lastChar = c;
167 return delegate.append(c);
168 }
169 }
136170 }
2323 import java.util.List;
2424 import java.util.Map;
2525 import java.util.Set;
26 import java.util.stream.Collectors;
2627 import javax.lang.model.SourceVersion;
2728 import javax.lang.model.element.Element;
2829 import javax.lang.model.element.ExecutableElement;
8182
8283 void emit(CodeWriter codeWriter, String enclosingName, Set<Modifier> implicitModifiers)
8384 throws IOException {
84 codeWriter.emitJavadoc(javadoc);
85 codeWriter.emitJavadoc(javadocWithParameters());
8586 codeWriter.emitAnnotations(annotations, false);
8687 codeWriter.emitModifiers(modifiers, implicitModifiers);
8788
131132 codeWriter.emit(" {\n");
132133
133134 codeWriter.indent();
134 codeWriter.emit(code);
135 codeWriter.emit(code, true);
135136 codeWriter.unindent();
136137
137138 codeWriter.emit("}\n");
138139 }
140 codeWriter.popTypeVariables(typeVariables);
141 }
142
143 private CodeBlock javadocWithParameters() {
144 CodeBlock.Builder builder = javadoc.toBuilder();
145 boolean emitTagNewline = true;
146 for (ParameterSpec parameterSpec : parameters) {
147 if (!parameterSpec.javadoc.isEmpty()) {
148 // Emit a new line before @param section only if the method javadoc is present.
149 if (emitTagNewline && !javadoc.isEmpty()) builder.add("\n");
150 emitTagNewline = false;
151 builder.add("@param $L $L", parameterSpec.name, parameterSpec.javadoc);
152 }
153 }
154 return builder.build();
139155 }
140156
141157 public boolean hasModifier(Modifier modifier) {
216232 }
217233
218234 methodBuilder.returns(TypeName.get(method.getReturnType()));
219 methodBuilder.addParameters(ParameterSpec.parametersOf(method));
235 // Copying parameter annotations from the overridden method can be incorrect so we're
236 // deliberately dropping them. See https://github.com/square/javapoet/issues/482.
237 methodBuilder.addParameters(ParameterSpec.parametersOf(method)
238 .stream()
239 .map(parameterSpec -> {
240 ParameterSpec.Builder builder = parameterSpec.toBuilder();
241 builder.annotations.clear();
242 return builder.build();
243 })
244 .collect(Collectors.toList()));
220245 methodBuilder.varargs(method.isVarArgs());
221246
222247 for (TypeMirror thrownType : method.getThrownTypes()) {
276301 }
277302
278303 public static final class Builder {
279 private final String name;
304 private String name;
280305
281306 private final CodeBlock.Builder javadoc = CodeBlock.builder();
282 private final List<AnnotationSpec> annotations = new ArrayList<>();
283 private final List<Modifier> modifiers = new ArrayList<>();
284 private List<TypeVariableName> typeVariables = new ArrayList<>();
285307 private TypeName returnType;
286 private final List<ParameterSpec> parameters = new ArrayList<>();
287308 private final Set<TypeName> exceptions = new LinkedHashSet<>();
288309 private final CodeBlock.Builder code = CodeBlock.builder();
289310 private boolean varargs;
290311 private CodeBlock defaultValue;
291312
313 public final List<TypeVariableName> typeVariables = new ArrayList<>();
314 public final List<AnnotationSpec> annotations = new ArrayList<>();
315 public final List<Modifier> modifiers = new ArrayList<>();
316 public final List<ParameterSpec> parameters = new ArrayList<>();
317
292318 private Builder(String name) {
319 setName(name);
320 }
321
322 public Builder setName(String name) {
293323 checkNotNull(name, "name == null");
294324 checkArgument(name.equals(CONSTRUCTOR) || SourceVersion.isName(name),
295325 "not a valid name: %s", name);
296326 this.name = name;
297327 this.returnType = name.equals(CONSTRUCTOR) ? null : TypeName.VOID;
328 return this;
298329 }
299330
300331 public Builder addJavadoc(String format, Object... args) {
453484 }
454485
455486 /**
487 * @param codeBlock the control flow construct and its code, such as "if (foo == 5)".
488 * Shouldn't contain braces or newline characters.
489 */
490 public Builder beginControlFlow(CodeBlock codeBlock) {
491 return beginControlFlow("$L", codeBlock);
492 }
493
494 /**
456495 * @param controlFlow the control flow construct and its code, such as "else if (foo == 10)".
457496 * Shouldn't contain braces or newline characters.
458497 */
459498 public Builder nextControlFlow(String controlFlow, Object... args) {
460499 code.nextControlFlow(controlFlow, args);
461500 return this;
501 }
502
503 /**
504 * @param codeBlock the control flow construct and its code, such as "else if (foo == 10)".
505 * Shouldn't contain braces or newline characters.
506 */
507 public Builder nextControlFlow(CodeBlock codeBlock) {
508 return nextControlFlow("$L", codeBlock);
462509 }
463510
464511 public Builder endControlFlow() {
475522 return this;
476523 }
477524
525 /**
526 * @param codeBlock the optional control flow construct and its code, such as
527 * "while(foo == 20)". Only used for "do/while" control flows.
528 */
529 public Builder endControlFlow(CodeBlock codeBlock) {
530 return endControlFlow("$L", codeBlock);
531 }
532
478533 public Builder addStatement(String format, Object... args) {
479534 code.addStatement(format, args);
480535 return this;
2020 import java.util.Collections;
2121 import java.util.List;
2222 import java.util.Set;
23 import java.util.stream.Collectors;
2324 import javax.lang.model.SourceVersion;
25 import javax.lang.model.element.ElementKind;
2426 import javax.lang.model.element.ExecutableElement;
2527 import javax.lang.model.element.Modifier;
2628 import javax.lang.model.element.VariableElement;
3436 public final List<AnnotationSpec> annotations;
3537 public final Set<Modifier> modifiers;
3638 public final TypeName type;
39 public final CodeBlock javadoc;
3740
3841 private ParameterSpec(Builder builder) {
3942 this.name = checkNotNull(builder.name, "name == null");
4043 this.annotations = Util.immutableList(builder.annotations);
4144 this.modifiers = Util.immutableSet(builder.modifiers);
4245 this.type = checkNotNull(builder.type, "type == null");
46 this.javadoc = builder.javadoc.build();
4347 }
4448
4549 public boolean hasModifier(Modifier modifier) {
8084 }
8185
8286 public static ParameterSpec get(VariableElement element) {
87 checkArgument(element.getKind().equals(ElementKind.PARAMETER), "element is not a parameter");
88
89 // Copy over any annotations from element.
90 List<AnnotationSpec> annotations = element.getAnnotationMirrors()
91 .stream()
92 .map((mirror) -> AnnotationSpec.get(mirror))
93 .collect(Collectors.toList());
94
8395 TypeName type = TypeName.get(element.asType());
8496 String name = element.getSimpleName().toString();
8597 return ParameterSpec.builder(type, name)
8698 .addModifiers(element.getModifiers())
99 .addAnnotations(annotations)
87100 .build();
88101 }
89102
120133 public static final class Builder {
121134 private final TypeName type;
122135 private final String name;
136 private final CodeBlock.Builder javadoc = CodeBlock.builder();
123137
124 private final List<AnnotationSpec> annotations = new ArrayList<>();
125 private final List<Modifier> modifiers = new ArrayList<>();
138 public final List<AnnotationSpec> annotations = new ArrayList<>();
139 public final List<Modifier> modifiers = new ArrayList<>();
126140
127141 private Builder(TypeName type, String name) {
128142 this.type = type;
129143 this.name = name;
144 }
145
146 public Builder addJavadoc(String format, Object... args) {
147 javadoc.add(format, args);
148 return this;
149 }
150
151 public Builder addJavadoc(CodeBlock block) {
152 javadoc.add(block);
153 return this;
130154 }
131155
132156 public Builder addAnnotations(Iterable<AnnotationSpec> annotationSpecs) {
159183 public Builder addModifiers(Iterable<Modifier> modifiers) {
160184 checkNotNull(modifiers, "modifiers == null");
161185 for (Modifier modifier : modifiers) {
186 if (!modifier.equals(Modifier.FINAL)) {
187 throw new IllegalStateException("unexpected parameter modifier: " + modifier);
188 }
162189 this.modifiers.add(modifier);
163190 }
164191 return this;
4343 * identifies composite types like {@code char[]} and {@code Set<Long>}.
4444 *
4545 * <p>Type names are dumb identifiers only and do not model the values they name. For example, the
46 * type name for {@code java.lang.List} doesn't know about the {@code size()} method, the fact that
46 * type name for {@code java.util.List} doesn't know about the {@code size()} method, the fact that
4747 * lists are collections, or even that it accepts a single type parameter.
4848 *
4949 * <p>Instances of this class are immutable value objects that implement {@code equals()} and {@code
1515 package com.squareup.javapoet;
1616
1717 import java.io.IOException;
18 import java.lang.reflect.ParameterizedType;
1819 import java.lang.reflect.Type;
1920 import java.util.ArrayList;
2021 import java.util.Arrays;
2122 import java.util.Collections;
2223 import java.util.EnumSet;
24 import java.util.HashSet;
2325 import java.util.Iterator;
2426 import java.util.LinkedHashMap;
27 import java.util.LinkedHashSet;
2528 import java.util.List;
2629 import java.util.Locale;
2730 import java.util.Map;
2932 import javax.lang.model.SourceVersion;
3033 import javax.lang.model.element.Element;
3134 import javax.lang.model.element.Modifier;
35 import javax.lang.model.element.TypeElement;
36 import javax.lang.model.type.DeclaredType;
37 import javax.lang.model.type.NoType;
38 import javax.lang.model.type.TypeMirror;
39 import javax.lang.model.util.ElementFilter;
3240
3341 import static com.squareup.javapoet.Util.checkArgument;
3442 import static com.squareup.javapoet.Util.checkNotNull;
5260 public final CodeBlock initializerBlock;
5361 public final List<MethodSpec> methodSpecs;
5462 public final List<TypeSpec> typeSpecs;
63 final Set<String> nestedTypesSimpleNames;
5564 public final List<Element> originatingElements;
65 public final Set<String> alwaysQualifiedNames;
5666
5767 private TypeSpec(Builder builder) {
5868 this.kind = builder.kind;
7080 this.initializerBlock = builder.initializerBlock.build();
7181 this.methodSpecs = Util.immutableList(builder.methodSpecs);
7282 this.typeSpecs = Util.immutableList(builder.typeSpecs);
73
83 this.alwaysQualifiedNames = Util.immutableSet(builder.alwaysQualifiedNames);
84
85 nestedTypesSimpleNames = new HashSet<>(builder.typeSpecs.size());
7486 List<Element> originatingElementsMutable = new ArrayList<>();
7587 originatingElementsMutable.addAll(builder.originatingElements);
7688 for (TypeSpec typeSpec : builder.typeSpecs) {
89 nestedTypesSimpleNames.add(typeSpec.name);
7790 originatingElementsMutable.addAll(typeSpec.originatingElements);
7891 }
92
7993 this.originatingElements = Util.immutableList(originatingElementsMutable);
8094 }
8195
101115 this.methodSpecs = Collections.emptyList();
102116 this.typeSpecs = Collections.emptyList();
103117 this.originatingElements = Collections.emptyList();
118 this.nestedTypesSimpleNames = Collections.emptySet();
119 this.alwaysQualifiedNames = Collections.emptySet();
104120 }
105121
106122 public boolean hasModifier(Modifier modifier) {
132148 }
133149
134150 public static Builder anonymousClassBuilder(String typeArgumentsFormat, Object... args) {
135 return anonymousClassBuilder(CodeBlock.builder()
136 .add(typeArgumentsFormat, args)
137 .build());
151 return anonymousClassBuilder(CodeBlock.of(typeArgumentsFormat, args));
138152 }
139153
140154 public static Builder anonymousClassBuilder(CodeBlock typeArguments) {
163177 builder.typeSpecs.addAll(typeSpecs);
164178 builder.initializerBlock.add(initializerBlock);
165179 builder.staticBlock.add(staticBlock);
180 builder.originatingElements.addAll(originatingElements);
181 builder.alwaysQualifiedNames.addAll(alwaysQualifiedNames);
166182 return builder;
167183 }
168184
315331
316332 codeWriter.unindent();
317333 codeWriter.popType();
334 codeWriter.popTypeVariables(typeVariables);
318335
319336 codeWriter.emit("}");
320337 if (enumName == null && anonymousTypeArguments == null) {
394411 private final CodeBlock anonymousTypeArguments;
395412
396413 private final CodeBlock.Builder javadoc = CodeBlock.builder();
397 private final List<AnnotationSpec> annotations = new ArrayList<>();
398 private final List<Modifier> modifiers = new ArrayList<>();
399 private final List<TypeVariableName> typeVariables = new ArrayList<>();
400414 private TypeName superclass = ClassName.OBJECT;
401 private final List<TypeName> superinterfaces = new ArrayList<>();
402 private final Map<String, TypeSpec> enumConstants = new LinkedHashMap<>();
403 private final List<FieldSpec> fieldSpecs = new ArrayList<>();
404415 private final CodeBlock.Builder staticBlock = CodeBlock.builder();
405416 private final CodeBlock.Builder initializerBlock = CodeBlock.builder();
406 private final List<MethodSpec> methodSpecs = new ArrayList<>();
407 private final List<TypeSpec> typeSpecs = new ArrayList<>();
408 private final List<Element> originatingElements = new ArrayList<>();
417
418 public final Map<String, TypeSpec> enumConstants = new LinkedHashMap<>();
419 public final List<AnnotationSpec> annotations = new ArrayList<>();
420 public final List<Modifier> modifiers = new ArrayList<>();
421 public final List<TypeVariableName> typeVariables = new ArrayList<>();
422 public final List<TypeName> superinterfaces = new ArrayList<>();
423 public final List<FieldSpec> fieldSpecs = new ArrayList<>();
424 public final List<MethodSpec> methodSpecs = new ArrayList<>();
425 public final List<TypeSpec> typeSpecs = new ArrayList<>();
426 public final List<Element> originatingElements = new ArrayList<>();
427 public final Set<String> alwaysQualifiedNames = new LinkedHashSet<>();
409428
410429 private Builder(Kind kind, String name,
411430 CodeBlock anonymousTypeArguments) {
448467 }
449468
450469 public Builder addModifiers(Modifier... modifiers) {
451 checkState(anonymousTypeArguments == null, "forbidden on anonymous types.");
452 for (Modifier modifier : modifiers) {
453 checkArgument(modifier != null, "modifiers contain null");
454 this.modifiers.add(modifier);
455 }
470 Collections.addAll(this.modifiers, modifiers);
456471 return this;
457472 }
458473
459474 public Builder addTypeVariables(Iterable<TypeVariableName> typeVariables) {
460 checkState(anonymousTypeArguments == null, "forbidden on anonymous types.");
461475 checkArgument(typeVariables != null, "typeVariables == null");
462476 for (TypeVariableName typeVariable : typeVariables) {
463477 this.typeVariables.add(typeVariable);
466480 }
467481
468482 public Builder addTypeVariable(TypeVariableName typeVariable) {
469 checkState(anonymousTypeArguments == null, "forbidden on anonymous types.");
470483 typeVariables.add(typeVariable);
471484 return this;
472485 }
481494 }
482495
483496 public Builder superclass(Type superclass) {
484 return superclass(TypeName.get(superclass));
497 return superclass(superclass, true);
498 }
499
500 public Builder superclass(Type superclass, boolean avoidNestedTypeNameClashes) {
501 superclass(TypeName.get(superclass));
502 if (avoidNestedTypeNameClashes) {
503 Class<?> clazz = getRawType(superclass);
504 if (clazz != null) {
505 avoidClashesWithNestedClasses(clazz);
506 }
507 }
508 return this;
509 }
510
511 public Builder superclass(TypeMirror superclass) {
512 return superclass(superclass, true);
513 }
514
515 public Builder superclass(TypeMirror superclass, boolean avoidNestedTypeNameClashes) {
516 superclass(TypeName.get(superclass));
517 if (avoidNestedTypeNameClashes && superclass instanceof DeclaredType) {
518 TypeElement superInterfaceElement =
519 (TypeElement) ((DeclaredType) superclass).asElement();
520 avoidClashesWithNestedClasses(superInterfaceElement);
521 }
522 return this;
485523 }
486524
487525 public Builder addSuperinterfaces(Iterable<? extends TypeName> superinterfaces) {
499537 }
500538
501539 public Builder addSuperinterface(Type superinterface) {
502 return addSuperinterface(TypeName.get(superinterface));
540 return addSuperinterface(superinterface, true);
541 }
542
543 public Builder addSuperinterface(Type superinterface, boolean avoidNestedTypeNameClashes) {
544 addSuperinterface(TypeName.get(superinterface));
545 if (avoidNestedTypeNameClashes) {
546 Class<?> clazz = getRawType(superinterface);
547 if (clazz != null) {
548 avoidClashesWithNestedClasses(clazz);
549 }
550 }
551 return this;
552 }
553
554 private Class<?> getRawType(Type type) {
555 if (type instanceof Class<?>) {
556 return (Class<?>) type;
557 } else if (type instanceof ParameterizedType) {
558 return getRawType(((ParameterizedType) type).getRawType());
559 } else {
560 return null;
561 }
562 }
563
564 public Builder addSuperinterface(TypeMirror superinterface) {
565 return addSuperinterface(superinterface, true);
566 }
567
568 public Builder addSuperinterface(TypeMirror superinterface,
569 boolean avoidNestedTypeNameClashes) {
570 addSuperinterface(TypeName.get(superinterface));
571 if (avoidNestedTypeNameClashes && superinterface instanceof DeclaredType) {
572 TypeElement superInterfaceElement =
573 (TypeElement) ((DeclaredType) superinterface).asElement();
574 avoidClashesWithNestedClasses(superInterfaceElement);
575 }
576 return this;
503577 }
504578
505579 public Builder addEnumConstant(String name) {
507581 }
508582
509583 public Builder addEnumConstant(String name, TypeSpec typeSpec) {
510 checkState(kind == Kind.ENUM, "%s is not enum", this.name);
511 checkArgument(typeSpec.anonymousTypeArguments != null,
512 "enum constants must have anonymous type arguments");
513 checkArgument(SourceVersion.isName(name), "not a valid enum constant: %s", name);
514584 enumConstants.put(name, typeSpec);
515585 return this;
516586 }
524594 }
525595
526596 public Builder addField(FieldSpec fieldSpec) {
527 if (kind == Kind.INTERFACE || kind == Kind.ANNOTATION) {
528 requireExactlyOneOf(fieldSpec.modifiers, Modifier.PUBLIC, Modifier.PRIVATE);
529 Set<Modifier> check = EnumSet.of(Modifier.STATIC, Modifier.FINAL);
530 checkState(fieldSpec.modifiers.containsAll(check), "%s %s.%s requires modifiers %s",
531 kind, name, fieldSpec.name, check);
532 }
533597 fieldSpecs.add(fieldSpec);
534598 return this;
535599 }
568632 }
569633
570634 public Builder addMethod(MethodSpec methodSpec) {
571 if (kind == Kind.INTERFACE) {
572 requireExactlyOneOf(methodSpec.modifiers, Modifier.ABSTRACT, Modifier.STATIC,
573 Modifier.DEFAULT);
574 requireExactlyOneOf(methodSpec.modifiers, Modifier.PUBLIC, Modifier.PRIVATE);
575 } else if (kind == Kind.ANNOTATION) {
576 checkState(methodSpec.modifiers.equals(kind.implicitMethodModifiers),
577 "%s %s.%s requires modifiers %s",
578 kind, name, methodSpec.name, kind.implicitMethodModifiers);
579 }
580 if (kind != Kind.ANNOTATION) {
581 checkState(methodSpec.defaultValue == null, "%s %s.%s cannot have a default value",
582 kind, name, methodSpec.name);
583 }
584 if (kind != Kind.INTERFACE) {
585 checkState(!methodSpec.hasModifier(Modifier.DEFAULT), "%s %s.%s cannot be default",
586 kind, name, methodSpec.name);
587 }
588635 methodSpecs.add(methodSpec);
589636 return this;
590637 }
598645 }
599646
600647 public Builder addType(TypeSpec typeSpec) {
601 checkArgument(typeSpec.modifiers.containsAll(kind.implicitTypeModifiers),
602 "%s %s.%s requires modifiers %s", kind, name, typeSpec.name,
603 kind.implicitTypeModifiers);
604648 typeSpecs.add(typeSpec);
605649 return this;
606650 }
610654 return this;
611655 }
612656
657 public Builder alwaysQualify(String... simpleNames) {
658 checkArgument(simpleNames != null, "simpleNames == null");
659 for (String name : simpleNames) {
660 checkArgument(
661 name != null,
662 "null entry in simpleNames array: %s",
663 Arrays.toString(simpleNames)
664 );
665 alwaysQualifiedNames.add(name);
666 }
667 return this;
668 }
669
670 /**
671 * Call this to always fully qualify any types that would conflict with possibly nested types of
672 * this {@code typeElement}. For example - if the following type was passed in as the
673 * typeElement:
674 *
675 * <pre><code>
676 * class Foo {
677 * class NestedTypeA {
678 *
679 * }
680 * class NestedTypeB {
681 *
682 * }
683 * }
684 * </code></pre>
685 *
686 * <p>
687 * Then this would add {@code "NestedTypeA"} and {@code "NestedTypeB"} as names that should
688 * always be qualified via {@link #alwaysQualify(String...)}. This way they would avoid
689 * possible import conflicts when this JavaFile is written.
690 *
691 * @param typeElement the {@link TypeElement} with nested types to avoid clashes with.
692 * @return this builder instance.
693 */
694 public Builder avoidClashesWithNestedClasses(TypeElement typeElement) {
695 checkArgument(typeElement != null, "typeElement == null");
696 for (TypeElement nestedType : ElementFilter.typesIn(typeElement.getEnclosedElements())) {
697 alwaysQualify(nestedType.getSimpleName().toString());
698 }
699 TypeMirror superclass = typeElement.getSuperclass();
700 if (!(superclass instanceof NoType) && superclass instanceof DeclaredType) {
701 TypeElement superclassElement = (TypeElement) ((DeclaredType) superclass).asElement();
702 avoidClashesWithNestedClasses(superclassElement);
703 }
704 for (TypeMirror superinterface : typeElement.getInterfaces()) {
705 if (superinterface instanceof DeclaredType) {
706 TypeElement superinterfaceElement
707 = (TypeElement) ((DeclaredType) superinterface).asElement();
708 avoidClashesWithNestedClasses(superinterfaceElement);
709 }
710 }
711 return this;
712 }
713
714 /**
715 * Call this to always fully qualify any types that would conflict with possibly nested types of
716 * this {@code typeElement}. For example - if the following type was passed in as the
717 * typeElement:
718 *
719 * <pre><code>
720 * class Foo {
721 * class NestedTypeA {
722 *
723 * }
724 * class NestedTypeB {
725 *
726 * }
727 * }
728 * </code></pre>
729 *
730 * <p>
731 * Then this would add {@code "NestedTypeA"} and {@code "NestedTypeB"} as names that should
732 * always be qualified via {@link #alwaysQualify(String...)}. This way they would avoid
733 * possible import conflicts when this JavaFile is written.
734 *
735 * @param clazz the {@link Class} with nested types to avoid clashes with.
736 * @return this builder instance.
737 */
738 public Builder avoidClashesWithNestedClasses(Class<?> clazz) {
739 checkArgument(clazz != null, "clazz == null");
740 for (Class<?> nestedType : clazz.getDeclaredClasses()) {
741 alwaysQualify(nestedType.getSimpleName());
742 }
743 Class<?> superclass = clazz.getSuperclass();
744 if (superclass != null && !Object.class.equals(superclass)) {
745 avoidClashesWithNestedClasses(superclass);
746 }
747 for (Class<?> superinterface : clazz.getInterfaces()) {
748 avoidClashesWithNestedClasses(superinterface);
749 }
750 return this;
751 }
752
613753 public TypeSpec build() {
754 for (AnnotationSpec annotationSpec : annotations) {
755 checkNotNull(annotationSpec, "annotationSpec == null");
756 }
757
758 if (!modifiers.isEmpty()) {
759 checkState(anonymousTypeArguments == null, "forbidden on anonymous types.");
760 for (Modifier modifier : modifiers) {
761 checkArgument(modifier != null, "modifiers contain null");
762 }
763 }
764
614765 checkArgument(kind != Kind.ENUM || !enumConstants.isEmpty(),
615766 "at least one enum constant is required for %s", name);
767
768 for (TypeName superinterface : superinterfaces) {
769 checkArgument(superinterface != null, "superinterfaces contains null");
770 }
771
772 if (!typeVariables.isEmpty()) {
773 checkState(anonymousTypeArguments == null,
774 "typevariables are forbidden on anonymous types.");
775 for (TypeVariableName typeVariableName : typeVariables) {
776 checkArgument(typeVariableName != null, "typeVariables contain null");
777 }
778 }
779
780 for (Map.Entry<String, TypeSpec> enumConstant : enumConstants.entrySet()) {
781 checkState(kind == Kind.ENUM, "%s is not enum", this.name);
782 checkArgument(enumConstant.getValue().anonymousTypeArguments != null,
783 "enum constants must have anonymous type arguments");
784 checkArgument(SourceVersion.isName(name), "not a valid enum constant: %s", name);
785 }
786
787 for (FieldSpec fieldSpec : fieldSpecs) {
788 if (kind == Kind.INTERFACE || kind == Kind.ANNOTATION) {
789 requireExactlyOneOf(fieldSpec.modifiers, Modifier.PUBLIC, Modifier.PRIVATE);
790 Set<Modifier> check = EnumSet.of(Modifier.STATIC, Modifier.FINAL);
791 checkState(fieldSpec.modifiers.containsAll(check), "%s %s.%s requires modifiers %s",
792 kind, name, fieldSpec.name, check);
793 }
794 }
795
796 for (MethodSpec methodSpec : methodSpecs) {
797 if (kind == Kind.INTERFACE) {
798 requireExactlyOneOf(methodSpec.modifiers, Modifier.ABSTRACT, Modifier.STATIC,
799 Modifier.DEFAULT);
800 requireExactlyOneOf(methodSpec.modifiers, Modifier.PUBLIC, Modifier.PRIVATE);
801 } else if (kind == Kind.ANNOTATION) {
802 checkState(methodSpec.modifiers.equals(kind.implicitMethodModifiers),
803 "%s %s.%s requires modifiers %s",
804 kind, name, methodSpec.name, kind.implicitMethodModifiers);
805 }
806 if (kind != Kind.ANNOTATION) {
807 checkState(methodSpec.defaultValue == null, "%s %s.%s cannot have a default value",
808 kind, name, methodSpec.name);
809 }
810 if (kind != Kind.INTERFACE) {
811 checkState(!methodSpec.hasModifier(Modifier.DEFAULT), "%s %s.%s cannot be default",
812 kind, name, methodSpec.name);
813 }
814 }
815
816 for (TypeSpec typeSpec : typeSpecs) {
817 checkArgument(typeSpec.modifiers.containsAll(kind.implicitTypeModifiers),
818 "%s %s.%s requires modifiers %s", kind, name, typeSpec.name,
819 kind.implicitTypeModifiers);
820 }
616821
617822 boolean isAbstract = modifiers.contains(Modifier.ABSTRACT) || kind != Kind.CLASS;
618823 for (MethodSpec methodSpec : methodSpecs) {
0 /*
1 * Copyright (C) 2019 Square, Inc.
2 *
3 * Licensed under the Apache License, Version 2.0 (the "License");
4 * you may not use this file except in compliance with the License.
5 * You may obtain a copy of the License at
6 *
7 * http://www.apache.org/licenses/LICENSE-2.0
8 *
9 * Unless required by applicable law or agreed to in writing, software
10 * distributed under the License is distributed on an "AS IS" BASIS,
11 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12 * See the License for the specific language governing permissions and
13 * limitations under the License.
14 */
15
16 import static com.google.common.truth.Truth.assertThat;
17
18 import com.squareup.javapoet.ClassName;
19 import org.junit.Test;
20
21 /**
22 * Since it is impossible to import classes from the default package into other
23 * modules, this test must live in this package.
24 */
25 public final class ClassNameNoPackageTest {
26 @Test public void shouldSupportClassInDefaultPackage() {
27 ClassName className = ClassName.get(ClassNameNoPackageTest.class);
28 assertThat(className.packageName()).isEqualTo("");
29 assertThat(className.simpleName()).isEqualTo("ClassNameNoPackageTest");
30 assertThat(className.toString()).isEqualTo("ClassNameNoPackageTest");
31 }
32 }
1919 import java.lang.annotation.Inherited;
2020 import java.lang.annotation.Retention;
2121 import java.lang.annotation.RetentionPolicy;
22 import java.util.Arrays;
23
2224 import javax.lang.model.element.TypeElement;
2325 import org.junit.Rule;
2426 import org.junit.Test;
370372 }
371373 }
372374
375 @Test public void modifyMembers() {
376 AnnotationSpec.Builder builder = AnnotationSpec.builder(SuppressWarnings.class)
377 .addMember("value", "$S", "Foo");
378
379 builder.members.clear();
380 builder.members.put("value", Arrays.asList(CodeBlock.of("$S", "Bar")));
381
382 assertThat(builder.build().toString()).isEqualTo("@java.lang.SuppressWarnings(\"Bar\")");
383 }
384
373385 private String toString(TypeSpec typeSpec) {
374386 return JavaFile.builder("com.squareup.tacos", typeSpec).build().toString();
375387 }
192192 assertEquals("Foo$Bar$Baz", ClassName.get("", "Foo", "Bar", "Baz").reflectionName());
193193 assertEquals("a.b.c.Foo$Bar$Baz", ClassName.get("a.b.c", "Foo", "Bar", "Baz").reflectionName());
194194 }
195
196 @Test
197 public void canonicalName() {
198 assertEquals("java.lang.Object", TypeName.OBJECT.canonicalName());
199 assertEquals("java.lang.Thread.State", ClassName.get(Thread.State.class).canonicalName());
200 assertEquals("java.util.Map.Entry", ClassName.get(Map.Entry.class).canonicalName());
201 assertEquals("Foo", ClassName.get("", "Foo").canonicalName());
202 assertEquals("Foo.Bar.Baz", ClassName.get("", "Foo", "Bar", "Baz").canonicalName());
203 assertEquals("a.b.c.Foo.Bar.Baz", ClassName.get("a.b.c", "Foo", "Bar", "Baz").canonicalName());
204 }
195205 }
338338 CodeBlock joined = codeBlocks.stream().collect(CodeBlock.joining(" || ", "start {", "} end"));
339339 assertThat(joined.toString()).isEqualTo("start {\"hello\" || world.World || need tacos} end");
340340 }
341
342 @Test public void clear() {
343 CodeBlock block = CodeBlock.builder()
344 .addStatement("$S", "Test string")
345 .clear()
346 .build();
347
348 assertThat(block.toString()).isEmpty();
349 }
341350 }
0 package com.squareup.javapoet;
1
2 import org.junit.Test;
3
4 import java.io.IOException;
5
6 import static com.google.common.truth.Truth.assertThat;
7
8 public class CodeWriterTest {
9
10 @Test
11 public void emptyLineInJavaDocDosEndings() throws IOException {
12 CodeBlock javadocCodeBlock = CodeBlock.of("A\r\n\r\nB\r\n");
13 StringBuilder out = new StringBuilder();
14 new CodeWriter(out).emitJavadoc(javadocCodeBlock);
15 assertThat(out.toString()).isEqualTo(
16 "/**\n" +
17 " * A\n" +
18 " *\n" +
19 " * B\n" +
20 " */\n");
21 }
22 }
2727 FieldSpec b = FieldSpec.builder(int.class, "foo").build();
2828 assertThat(a.equals(b)).isTrue();
2929 assertThat(a.hashCode()).isEqualTo(b.hashCode());
30 assertThat(a.toString()).isEqualTo(b.toString());
3031 a = FieldSpec.builder(int.class, "FOO", Modifier.PUBLIC, Modifier.STATIC).build();
3132 b = FieldSpec.builder(int.class, "FOO", Modifier.PUBLIC, Modifier.STATIC).build();
3233 assertThat(a.equals(b)).isTrue();
3334 assertThat(a.hashCode()).isEqualTo(b.hashCode());
35 assertThat(a.toString()).isEqualTo(b.toString());
3436 }
3537
3638 @Test public void nullAnnotationsAddition() {
4345 .isEqualTo("annotationSpecs == null");
4446 }
4547 }
46 }
48
49 @Test public void modifyAnnotations() {
50 FieldSpec.Builder builder = FieldSpec.builder(int.class, "foo")
51 .addAnnotation(Override.class)
52 .addAnnotation(SuppressWarnings.class);
53
54 builder.annotations.remove(1);
55 assertThat(builder.build().annotations).hasSize(1);
56 }
57
58 @Test public void modifyModifiers() {
59 FieldSpec.Builder builder = FieldSpec.builder(int.class, "foo")
60 .addModifiers(Modifier.PUBLIC, Modifier.STATIC);
61
62 builder.modifiers.remove(1);
63 assertThat(builder.build().modifiers).containsExactly(Modifier.PUBLIC);
64 }
65 }
215215 + "class Taco {\n"
216216 + "}\n");
217217 }
218
219 @Test public void writeToPathReturnsPath() throws IOException {
220 JavaFile javaFile = JavaFile.builder("foo", TypeSpec.classBuilder("Taco").build()).build();
221 Path filePath = javaFile.writeToPath(fsRoot);
222 // Cast to avoid ambiguity between assertThat(Path) and assertThat(Iterable<?>)
223 assertThat((Iterable<?>) filePath).isEqualTo(fsRoot.resolve(fs.getPath("foo", "Taco.java")));
224 }
218225 }
1414 */
1515 package com.squareup.javapoet;
1616
17 import java.io.File;
18 import com.google.testing.compile.CompilationRule;
1719 import java.util.Collections;
1820 import java.util.Date;
1921 import java.util.List;
22 import java.util.Map;
23 import java.util.Optional;
2024 import java.util.concurrent.TimeUnit;
25 import java.util.regex.Pattern;
2126 import javax.lang.model.element.Modifier;
27 import javax.lang.model.element.TypeElement;
2228 import org.junit.Ignore;
29 import org.junit.Rule;
2330 import org.junit.Test;
2431 import org.junit.runner.RunWith;
2532 import org.junit.runners.JUnit4;
2835
2936 @RunWith(JUnit4.class)
3037 public final class JavaFileTest {
38
39 @Rule public final CompilationRule compilation = new CompilationRule();
40
41 private TypeElement getElement(Class<?> clazz) {
42 return compilation.getElements().getTypeElement(clazz.getCanonicalName());
43 }
44
3145 @Test public void importStaticReadmeExample() {
3246 ClassName hoverboard = ClassName.get("com.mattel", "Hoverboard");
3347 ClassName namedBoards = ClassName.get("com.mattel", "Hoverboard", "Boards");
688702 + " A a;\n"
689703 + "}\n");
690704 }
705
706 @Test public void modifyStaticImports() throws Exception {
707 JavaFile.Builder builder = JavaFile.builder("com.squareup.tacos",
708 TypeSpec.classBuilder("Taco")
709 .build())
710 .addStaticImport(File.class, "separator");
711
712 builder.staticImports.clear();
713 builder.staticImports.add(File.class.getCanonicalName() + ".separatorChar");
714
715 String source = builder.build().toString();
716
717 assertThat(source).isEqualTo(""
718 + "package com.squareup.tacos;\n"
719 + "\n"
720 + "import static java.io.File.separatorChar;\n"
721 + "\n"
722 + "class Taco {\n"
723 + "}\n");
724 }
725
726 @Test public void alwaysQualifySimple() {
727 String source = JavaFile.builder("com.squareup.tacos",
728 TypeSpec.classBuilder("Taco")
729 .addField(Thread.class, "thread")
730 .alwaysQualify("Thread")
731 .build())
732 .build()
733 .toString();
734 assertThat(source).isEqualTo(""
735 + "package com.squareup.tacos;\n"
736 + "\n"
737 + "class Taco {\n"
738 + " java.lang.Thread thread;\n"
739 + "}\n");
740 }
741
742 @Test public void alwaysQualifySupersedesJavaLangImports() {
743 String source = JavaFile.builder("com.squareup.tacos",
744 TypeSpec.classBuilder("Taco")
745 .addField(Thread.class, "thread")
746 .alwaysQualify("Thread")
747 .build())
748 .skipJavaLangImports(true)
749 .build()
750 .toString();
751 assertThat(source).isEqualTo(""
752 + "package com.squareup.tacos;\n"
753 + "\n"
754 + "class Taco {\n"
755 + " java.lang.Thread thread;\n"
756 + "}\n");
757 }
758
759 @Test public void avoidClashesWithNestedClasses_viaClass() {
760 String source = JavaFile.builder("com.squareup.tacos",
761 TypeSpec.classBuilder("Taco")
762 // These two should get qualified
763 .addField(ClassName.get("other", "NestedTypeA"), "nestedA")
764 .addField(ClassName.get("other", "NestedTypeB"), "nestedB")
765 // This one shouldn't since it's not a nested type of Foo
766 .addField(ClassName.get("other", "NestedTypeC"), "nestedC")
767 // This one shouldn't since we only look at nested types
768 .addField(ClassName.get("other", "Foo"), "foo")
769 .avoidClashesWithNestedClasses(Foo.class)
770 .build())
771 .build()
772 .toString();
773 assertThat(source).isEqualTo(""
774 + "package com.squareup.tacos;\n"
775 + "\n"
776 + "import other.Foo;\n"
777 + "import other.NestedTypeC;\n"
778 + "\n"
779 + "class Taco {\n"
780 + " other.NestedTypeA nestedA;\n"
781 + "\n"
782 + " other.NestedTypeB nestedB;\n"
783 + "\n"
784 + " NestedTypeC nestedC;\n"
785 + "\n"
786 + " Foo foo;\n"
787 + "}\n");
788 }
789
790 @Test public void avoidClashesWithNestedClasses_viaTypeElement() {
791 String source = JavaFile.builder("com.squareup.tacos",
792 TypeSpec.classBuilder("Taco")
793 // These two should get qualified
794 .addField(ClassName.get("other", "NestedTypeA"), "nestedA")
795 .addField(ClassName.get("other", "NestedTypeB"), "nestedB")
796 // This one shouldn't since it's not a nested type of Foo
797 .addField(ClassName.get("other", "NestedTypeC"), "nestedC")
798 // This one shouldn't since we only look at nested types
799 .addField(ClassName.get("other", "Foo"), "foo")
800 .avoidClashesWithNestedClasses(getElement(Foo.class))
801 .build())
802 .build()
803 .toString();
804 assertThat(source).isEqualTo(""
805 + "package com.squareup.tacos;\n"
806 + "\n"
807 + "import other.Foo;\n"
808 + "import other.NestedTypeC;\n"
809 + "\n"
810 + "class Taco {\n"
811 + " other.NestedTypeA nestedA;\n"
812 + "\n"
813 + " other.NestedTypeB nestedB;\n"
814 + "\n"
815 + " NestedTypeC nestedC;\n"
816 + "\n"
817 + " Foo foo;\n"
818 + "}\n");
819 }
820
821 @Test public void avoidClashesWithNestedClasses_viaSuperinterfaceType() {
822 String source = JavaFile.builder("com.squareup.tacos",
823 TypeSpec.classBuilder("Taco")
824 // These two should get qualified
825 .addField(ClassName.get("other", "NestedTypeA"), "nestedA")
826 .addField(ClassName.get("other", "NestedTypeB"), "nestedB")
827 // This one shouldn't since it's not a nested type of Foo
828 .addField(ClassName.get("other", "NestedTypeC"), "nestedC")
829 // This one shouldn't since we only look at nested types
830 .addField(ClassName.get("other", "Foo"), "foo")
831 .addType(TypeSpec.classBuilder("NestedTypeA").build())
832 .addType(TypeSpec.classBuilder("NestedTypeB").build())
833 .addSuperinterface(FooInterface.class)
834 .build())
835 .build()
836 .toString();
837 assertThat(source).isEqualTo("package com.squareup.tacos;\n"
838 + "\n"
839 + "import com.squareup.javapoet.JavaFileTest;\n"
840 + "import other.Foo;\n"
841 + "import other.NestedTypeC;\n"
842 + "\n"
843 + "class Taco implements JavaFileTest.FooInterface {\n"
844 + " other.NestedTypeA nestedA;\n"
845 + "\n"
846 + " other.NestedTypeB nestedB;\n"
847 + "\n"
848 + " NestedTypeC nestedC;\n"
849 + "\n"
850 + " Foo foo;\n"
851 + "\n"
852 + " class NestedTypeA {\n"
853 + " }\n"
854 + "\n"
855 + " class NestedTypeB {\n"
856 + " }\n"
857 + "}\n");
858 }
859
860 static class Foo {
861 static class NestedTypeA {
862
863 }
864 static class NestedTypeB {
865
866 }
867 }
868
869 interface FooInterface {
870 class NestedTypeA {
871
872 }
873 class NestedTypeB {
874
875 }
876 }
877
878 private TypeSpec.Builder childTypeBuilder() {
879 return TypeSpec.classBuilder("Child")
880 .addMethod(MethodSpec.methodBuilder("optionalString")
881 .returns(ParameterizedTypeName.get(Optional.class, String.class))
882 .addStatement("return $T.empty()", Optional.class)
883 .build())
884 .addMethod(MethodSpec.methodBuilder("pattern")
885 .returns(Pattern.class)
886 .addStatement("return null")
887 .build());
888 }
889
890 @Test
891 public void avoidClashes_parentChild_superclass_type() {
892 String source = JavaFile.builder("com.squareup.javapoet",
893 childTypeBuilder().superclass(Parent.class).build())
894 .build()
895 .toString();
896 assertThat(source).isEqualTo("package com.squareup.javapoet;\n"
897 + "\n"
898 + "import java.lang.String;\n"
899 + "\n"
900 + "class Child extends JavaFileTest.Parent {\n"
901 + " java.util.Optional<String> optionalString() {\n"
902 + " return java.util.Optional.empty();\n"
903 + " }\n"
904 + "\n"
905 + " java.util.regex.Pattern pattern() {\n"
906 + " return null;\n"
907 + " }\n"
908 + "}\n");
909 }
910
911 @Test
912 public void avoidClashes_parentChild_superclass_typeMirror() {
913 String source = JavaFile.builder("com.squareup.javapoet",
914 childTypeBuilder().superclass(getElement(Parent.class).asType()).build())
915 .build()
916 .toString();
917 assertThat(source).isEqualTo("package com.squareup.javapoet;\n"
918 + "\n"
919 + "import java.lang.String;\n"
920 + "\n"
921 + "class Child extends JavaFileTest.Parent {\n"
922 + " java.util.Optional<String> optionalString() {\n"
923 + " return java.util.Optional.empty();\n"
924 + " }\n"
925 + "\n"
926 + " java.util.regex.Pattern pattern() {\n"
927 + " return null;\n"
928 + " }\n"
929 + "}\n");
930 }
931
932 @Test
933 public void avoidClashes_parentChild_superinterface_type() {
934 String source = JavaFile.builder("com.squareup.javapoet",
935 childTypeBuilder().addSuperinterface(ParentInterface.class).build())
936 .build()
937 .toString();
938 assertThat(source).isEqualTo("package com.squareup.javapoet;\n"
939 + "\n"
940 + "import java.lang.String;\n"
941 + "import java.util.regex.Pattern;\n"
942 + "\n"
943 + "class Child implements JavaFileTest.ParentInterface {\n"
944 + " java.util.Optional<String> optionalString() {\n"
945 + " return java.util.Optional.empty();\n"
946 + " }\n"
947 + "\n"
948 + " Pattern pattern() {\n"
949 + " return null;\n"
950 + " }\n"
951 + "}\n");
952 }
953
954 @Test
955 public void avoidClashes_parentChild_superinterface_typeMirror() {
956 String source = JavaFile.builder("com.squareup.javapoet",
957 childTypeBuilder().addSuperinterface(getElement(ParentInterface.class).asType()).build())
958 .build()
959 .toString();
960 assertThat(source).isEqualTo("package com.squareup.javapoet;\n"
961 + "\n"
962 + "import java.lang.String;\n"
963 + "import java.util.regex.Pattern;\n"
964 + "\n"
965 + "class Child implements JavaFileTest.ParentInterface {\n"
966 + " java.util.Optional<String> optionalString() {\n"
967 + " return java.util.Optional.empty();\n"
968 + " }\n"
969 + "\n"
970 + " Pattern pattern() {\n"
971 + " return null;\n"
972 + " }\n"
973 + "}\n");
974 }
975
976 // Regression test for https://github.com/square/javapoet/issues/77
977 // This covers class and inheritance
978 static class Parent implements ParentInterface {
979 static class Pattern {
980
981 }
982 }
983
984 interface ParentInterface {
985 class Optional {
986
987 }
988 }
989
990 // Regression test for case raised here: https://github.com/square/javapoet/issues/77#issuecomment-519972404
991 @Test
992 public void avoidClashes_mapEntry() {
993 String source = JavaFile.builder("com.squareup.javapoet",
994 TypeSpec.classBuilder("MapType")
995 .addMethod(MethodSpec.methodBuilder("optionalString")
996 .returns(ClassName.get("com.foo", "Entry"))
997 .addStatement("return null")
998 .build())
999 .addSuperinterface(Map.class)
1000 .build())
1001 .build()
1002 .toString();
1003 assertThat(source).isEqualTo("package com.squareup.javapoet;\n"
1004 + "\n"
1005 + "import java.util.Map;\n"
1006 + "\n"
1007 + "class MapType implements Map {\n"
1008 + " com.foo.Entry optionalString() {\n"
1009 + " return null;\n"
1010 + " }\n"
1011 + "}\n");
1012 }
6911013 }
2020 import java.lang.annotation.ElementType;
2121 import java.lang.annotation.Target;
2222 import java.util.Arrays;
23 import java.util.Collection;
23 import java.util.HashMap;
2424 import java.util.List;
25 import java.util.Map;
2526 import java.util.concurrent.Callable;
2627 import java.util.concurrent.TimeoutException;
2728 import javax.lang.model.element.ExecutableElement;
3637
3738 import static com.google.common.collect.Iterables.getOnlyElement;
3839 import static com.google.common.truth.Truth.assertThat;
40 import static com.squareup.javapoet.MethodSpec.CONSTRUCTOR;
41 import static com.squareup.javapoet.TestUtil.findFirst;
3942 import static javax.lang.model.util.ElementFilter.methodsIn;
4043 import static org.junit.Assert.fail;
4144
5255
5356 private TypeElement getElement(Class<?> clazz) {
5457 return elements.getTypeElement(clazz.getCanonicalName());
55 }
56
57 private ExecutableElement findFirst(Collection<ExecutableElement> elements, String name) {
58 for (ExecutableElement executableElement : elements) {
59 if (executableElement.getSimpleName().toString().equals(name)) {
60 return executableElement;
61 }
62 }
63 throw new IllegalArgumentException(name + " not found in " + elements);
6458 }
6559
6660 @Test public void nullAnnotationsAddition() {
269263 assertThat(a.hashCode()).isEqualTo(b.hashCode());
270264 }
271265
266 @Test public void withoutParameterJavaDoc() {
267 MethodSpec methodSpec = MethodSpec.methodBuilder("getTaco")
268 .addModifiers(Modifier.PRIVATE)
269 .addParameter(TypeName.DOUBLE, "money")
270 .addJavadoc("Gets the best Taco\n")
271 .build();
272 assertThat(methodSpec.toString()).isEqualTo(""
273 + "/**\n"
274 + " * Gets the best Taco\n"
275 + " */\n"
276 + "private void getTaco(double money) {\n"
277 + "}\n");
278 }
279
280 @Test public void withParameterJavaDoc() {
281 MethodSpec methodSpec = MethodSpec.methodBuilder("getTaco")
282 .addParameter(ParameterSpec.builder(TypeName.DOUBLE, "money")
283 .addJavadoc("the amount required to buy the taco.\n")
284 .build())
285 .addParameter(ParameterSpec.builder(TypeName.INT, "count")
286 .addJavadoc("the number of Tacos to buy.\n")
287 .build())
288 .addJavadoc("Gets the best Taco money can buy.\n")
289 .build();
290 assertThat(methodSpec.toString()).isEqualTo(""
291 + "/**\n"
292 + " * Gets the best Taco money can buy.\n"
293 + " *\n"
294 + " * @param money the amount required to buy the taco.\n"
295 + " * @param count the number of Tacos to buy.\n"
296 + " */\n"
297 + "void getTaco(double money, int count) {\n"
298 + "}\n");
299 }
300
301 @Test public void withParameterJavaDocAndWithoutMethodJavadoc() {
302 MethodSpec methodSpec = MethodSpec.methodBuilder("getTaco")
303 .addParameter(ParameterSpec.builder(TypeName.DOUBLE, "money")
304 .addJavadoc("the amount required to buy the taco.\n")
305 .build())
306 .addParameter(ParameterSpec.builder(TypeName.INT, "count")
307 .addJavadoc("the number of Tacos to buy.\n")
308 .build())
309 .build();
310 assertThat(methodSpec.toString()).isEqualTo(""
311 + "/**\n"
312 + " * @param money the amount required to buy the taco.\n"
313 + " * @param count the number of Tacos to buy.\n"
314 + " */\n"
315 + "void getTaco(double money, int count) {\n"
316 + "}\n");
317 }
318
272319 @Test public void duplicateExceptionsIgnored() {
273320 ClassName ioException = ClassName.get(IOException.class);
274321 ClassName timeoutException = ClassName.get(TimeoutException.class);
301348 assertThat(e.getMessage()).isEqualTo("modifiers == null");
302349 }
303350 }
351
352 @Test public void modifyMethodName() {
353 MethodSpec methodSpec = MethodSpec.methodBuilder("initialMethod")
354 .build()
355 .toBuilder()
356 .setName("revisedMethod")
357 .build();
358
359 assertThat(methodSpec.toString()).isEqualTo("" + "void revisedMethod() {\n" + "}\n");
360 }
361
362 @Test public void modifyAnnotations() {
363 MethodSpec.Builder builder = MethodSpec.methodBuilder("foo")
364 .addAnnotation(Override.class)
365 .addAnnotation(SuppressWarnings.class);
366
367 builder.annotations.remove(1);
368 assertThat(builder.build().annotations).hasSize(1);
369 }
370
371 @Test public void modifyModifiers() {
372 MethodSpec.Builder builder = MethodSpec.methodBuilder("foo")
373 .addModifiers(Modifier.PUBLIC, Modifier.STATIC);
374
375 builder.modifiers.remove(1);
376 assertThat(builder.build().modifiers).containsExactly(Modifier.PUBLIC);
377 }
378
379 @Test public void modifyParameters() {
380 MethodSpec.Builder builder = MethodSpec.methodBuilder("foo")
381 .addParameter(int.class, "source");
382
383 builder.parameters.remove(0);
384 assertThat(builder.build().parameters).isEmpty();
385 }
386
387 @Test public void modifyTypeVariables() {
388 TypeVariableName t = TypeVariableName.get("T");
389 MethodSpec.Builder builder = MethodSpec.methodBuilder("foo")
390 .addTypeVariable(t)
391 .addTypeVariable(TypeVariableName.get("V"));
392
393 builder.typeVariables.remove(1);
394 assertThat(builder.build().typeVariables).containsExactly(t);
395 }
396
397 @Test public void ensureTrailingNewline() {
398 MethodSpec methodSpec = MethodSpec.methodBuilder("method")
399 .addCode("codeWithNoNewline();")
400 .build();
401
402 assertThat(methodSpec.toString()).isEqualTo(""
403 + "void method() {\n"
404 + " codeWithNoNewline();\n"
405 + "}\n");
406 }
407
408 /** Ensures that we don't add a duplicate newline if one is already present. */
409 @Test public void ensureTrailingNewlineWithExistingNewline() {
410 MethodSpec methodSpec = MethodSpec.methodBuilder("method")
411 .addCode("codeWithNoNewline();\n") // Have a newline already, so ensure we're not adding one
412 .build();
413
414 assertThat(methodSpec.toString()).isEqualTo(""
415 + "void method() {\n"
416 + " codeWithNoNewline();\n"
417 + "}\n");
418 }
419
420 @Test public void controlFlowWithNamedCodeBlocks() {
421 Map<String, Object> m = new HashMap<>();
422 m.put("field", "valueField");
423 m.put("threshold", "5");
424
425 MethodSpec methodSpec = MethodSpec.methodBuilder("method")
426 .beginControlFlow(named("if ($field:N > $threshold:L)", m))
427 .nextControlFlow(named("else if ($field:N == $threshold:L)", m))
428 .endControlFlow()
429 .build();
430
431 assertThat(methodSpec.toString()).isEqualTo(""
432 + "void method() {\n"
433 + " if (valueField > 5) {\n"
434 + " } else if (valueField == 5) {\n"
435 + " }\n"
436 + "}\n");
437 }
438
439 @Test public void doWhileWithNamedCodeBlocks() {
440 Map<String, Object> m = new HashMap<>();
441 m.put("field", "valueField");
442 m.put("threshold", "5");
443
444 MethodSpec methodSpec = MethodSpec.methodBuilder("method")
445 .beginControlFlow("do")
446 .addStatement(named("$field:N--", m))
447 .endControlFlow(named("while ($field:N > $threshold:L)", m))
448 .build();
449
450 assertThat(methodSpec.toString()).isEqualTo(""
451 + "void method() {\n" +
452 " do {\n" +
453 " valueField--;\n" +
454 " } while (valueField > 5);\n" +
455 "}\n");
456 }
457
458 private static CodeBlock named(String format, Map<String, ?> args){
459 return CodeBlock.builder().addNamed(format, args).build();
460 }
461
304462 }
1717 import org.junit.Test;
1818
1919 import static com.google.common.truth.Truth.assertThat;
20 import static org.junit.Assert.assertEquals;
2021 import static org.junit.Assert.fail;
2122
2223 public final class NameAllocatorTest {
24
2325 @Test public void usage() throws Exception {
2426 NameAllocator nameAllocator = new NameAllocator();
2527 assertThat(nameAllocator.newName("foo", 1)).isEqualTo("foo");
5860 @Test public void characterMappingInvalidStartButValidPart() throws Exception {
5961 NameAllocator nameAllocator = new NameAllocator();
6062 assertThat(nameAllocator.newName("1ab", 1)).isEqualTo("_1ab");
63 assertThat(nameAllocator.newName("a-1", 2)).isEqualTo("a_1");
6164 }
6265
6366 @Test public void characterMappingInvalidStartIsInvalidPart() throws Exception {
1414 */
1515 package com.squareup.javapoet;
1616
17 import com.google.testing.compile.CompilationRule;
18 import java.util.ArrayList;
19 import java.util.List;
20 import javax.annotation.Nullable;
21 import javax.lang.model.element.ExecutableElement;
22 import javax.lang.model.element.TypeElement;
23 import javax.lang.model.element.VariableElement;
24 import javax.lang.model.util.Elements;
25 import org.junit.Before;
26 import org.junit.Rule;
27 import javax.lang.model.element.Modifier;
1728 import org.junit.Test;
1829
1930 import static com.google.common.truth.Truth.assertThat;
31 import static com.squareup.javapoet.TestUtil.findFirst;
32 import static javax.lang.model.util.ElementFilter.fieldsIn;
33 import static javax.lang.model.util.ElementFilter.methodsIn;
2034 import static org.junit.Assert.fail;
2135
22 import javax.lang.model.element.Modifier;
36 public class ParameterSpecTest {
37 @Rule public final CompilationRule compilation = new CompilationRule();
2338
24 public class ParameterSpecTest {
39 private Elements elements;
40
41 @Before public void setUp() {
42 elements = compilation.getElements();
43 }
44
45 private TypeElement getElement(Class<?> clazz) {
46 return elements.getTypeElement(clazz.getCanonicalName());
47 }
48
2549 @Test public void equalsAndHashCode() {
2650 ParameterSpec a = ParameterSpec.builder(int.class, "foo").build();
2751 ParameterSpec b = ParameterSpec.builder(int.class, "foo").build();
2852 assertThat(a.equals(b)).isTrue();
2953 assertThat(a.hashCode()).isEqualTo(b.hashCode());
54 assertThat(a.toString()).isEqualTo(b.toString());
3055 a = ParameterSpec.builder(int.class, "i").addModifiers(Modifier.STATIC).build();
3156 b = ParameterSpec.builder(int.class, "i").addModifiers(Modifier.STATIC).build();
3257 assertThat(a.equals(b)).isTrue();
3358 assertThat(a.hashCode()).isEqualTo(b.hashCode());
59 assertThat(a.toString()).isEqualTo(b.toString());
3460 }
3561
3662 @Test public void nullAnnotationsAddition() {
4268 .isEqualTo("annotationSpecs == null");
4369 }
4470 }
45 }
71
72 final class VariableElementFieldClass {
73 String name;
74 }
75
76 @Test public void fieldVariableElement() {
77 TypeElement classElement = getElement(VariableElementFieldClass.class);
78 List<VariableElement> methods = fieldsIn(elements.getAllMembers(classElement));
79 VariableElement element = findFirst(methods, "name");
80
81 try {
82 ParameterSpec.get(element);
83 fail();
84 } catch (IllegalArgumentException exception) {
85 assertThat(exception).hasMessageThat().isEqualTo("element is not a parameter");
86 }
87 }
88
89 final class VariableElementParameterClass {
90 public void foo(@Nullable final String bar) {
91 }
92 }
93
94 @Test public void parameterVariableElement() {
95 TypeElement classElement = getElement(VariableElementParameterClass.class);
96 List<ExecutableElement> methods = methodsIn(elements.getAllMembers(classElement));
97 ExecutableElement element = findFirst(methods, "foo");
98 VariableElement parameterElement = element.getParameters().get(0);
99
100 assertThat(ParameterSpec.get(parameterElement).toString())
101 .isEqualTo("@javax.annotation.Nullable java.lang.String arg0");
102 }
103
104 @Test public void addNonFinalModifier() {
105 List<Modifier> modifiers = new ArrayList<>();
106 modifiers.add(Modifier.FINAL);
107 modifiers.add(Modifier.PUBLIC);
108
109 try {
110 ParameterSpec.builder(int.class, "foo")
111 .addModifiers(modifiers);
112 fail();
113 } catch (Exception e) {
114 assertThat(e.getMessage()).isEqualTo("unexpected parameter modifier: public");
115 }
116 }
117
118 @Test public void modifyAnnotations() {
119 ParameterSpec.Builder builder = ParameterSpec.builder(int.class, "foo")
120 .addAnnotation(Override.class)
121 .addAnnotation(SuppressWarnings.class);
122
123 builder.annotations.remove(1);
124 assertThat(builder.build().annotations).hasSize(1);
125 }
126
127 @Test public void modifyModifiers() {
128 ParameterSpec.Builder builder = ParameterSpec.builder(int.class, "foo")
129 .addModifiers(Modifier.PUBLIC, Modifier.STATIC);
130
131 builder.modifiers.remove(1);
132 assertThat(builder.build().modifiers).containsExactly(Modifier.PUBLIC);
133 }
134 }
0 package com.squareup.javapoet;
1
2 import javax.lang.model.element.Element;
3 import javax.lang.model.element.ExecutableElement;
4 import javax.lang.model.element.VariableElement;
5 import java.util.Collection;
6
7 final class TestUtil {
8 static <E extends Element> E findFirst(Collection<E> elements, String name) {
9 for (E element : elements) {
10 if (element.getSimpleName().toString().equals(name)) {
11 return element;
12 }
13 }
14 throw new IllegalArgumentException(name + " not found in " + elements);
15 }
16 }
1616
1717 import com.google.common.collect.ImmutableMap;
1818 import com.google.testing.compile.CompilationRule;
19 import java.io.File;
1920 import java.io.IOException;
2021 import java.io.Serializable;
2122 import java.math.BigDecimal;
984985 + "}\n");
985986 }
986987
988 @Test public void simpleNameConflictsWithTypeVariable() {
989 ClassName inPackage = ClassName.get("com.squareup.tacos", "InPackage");
990 ClassName otherType = ClassName.get("com.other", "OtherType");
991 ClassName methodInPackage = ClassName.get("com.squareup.tacos", "MethodInPackage");
992 ClassName methodOtherType = ClassName.get("com.other", "MethodOtherType");
993 TypeSpec gen = TypeSpec.classBuilder("Gen")
994 .addTypeVariable(TypeVariableName.get("InPackage"))
995 .addTypeVariable(TypeVariableName.get("OtherType"))
996 .addField(FieldSpec.builder(inPackage, "inPackage").build())
997 .addField(FieldSpec.builder(otherType, "otherType").build())
998 .addMethod(MethodSpec.methodBuilder("withTypeVariables")
999 .addTypeVariable(TypeVariableName.get("MethodInPackage"))
1000 .addTypeVariable(TypeVariableName.get("MethodOtherType"))
1001 .addStatement("$T inPackage = null", methodInPackage)
1002 .addStatement("$T otherType = null", methodOtherType)
1003 .build())
1004 .addMethod(MethodSpec.methodBuilder("withoutTypeVariables")
1005 .addStatement("$T inPackage = null", methodInPackage)
1006 .addStatement("$T otherType = null", methodOtherType)
1007 .build())
1008 .addMethod(MethodSpec.methodBuilder("againWithTypeVariables")
1009 .addTypeVariable(TypeVariableName.get("MethodInPackage"))
1010 .addTypeVariable(TypeVariableName.get("MethodOtherType"))
1011 .addStatement("$T inPackage = null", methodInPackage)
1012 .addStatement("$T otherType = null", methodOtherType)
1013 .build())
1014 // https://github.com/square/javapoet/pull/657#discussion_r205514292
1015 .addMethod(MethodSpec.methodBuilder("masksEnclosingTypeVariable")
1016 .addTypeVariable(TypeVariableName.get("InPackage"))
1017 .build())
1018 .addMethod(MethodSpec.methodBuilder("hasSimpleNameThatWasPreviouslyMasked")
1019 .addStatement("$T inPackage = null", inPackage)
1020 .build())
1021 .build();
1022 assertThat(toString(gen)).isEqualTo(""
1023 + "package com.squareup.tacos;\n"
1024 + "\n"
1025 + "import com.other.MethodOtherType;\n"
1026 + "\n"
1027 + "class Gen<InPackage, OtherType> {\n"
1028 + " com.squareup.tacos.InPackage inPackage;\n"
1029 + "\n"
1030 + " com.other.OtherType otherType;\n"
1031 + "\n"
1032 + " <MethodInPackage, MethodOtherType> void withTypeVariables() {\n"
1033 + " com.squareup.tacos.MethodInPackage inPackage = null;\n"
1034 + " com.other.MethodOtherType otherType = null;\n"
1035 + " }\n"
1036 + "\n"
1037 + " void withoutTypeVariables() {\n"
1038 + " MethodInPackage inPackage = null;\n"
1039 + " MethodOtherType otherType = null;\n"
1040 + " }\n"
1041 + "\n"
1042 + " <MethodInPackage, MethodOtherType> void againWithTypeVariables() {\n"
1043 + " com.squareup.tacos.MethodInPackage inPackage = null;\n"
1044 + " com.other.MethodOtherType otherType = null;\n"
1045 + " }\n"
1046 + "\n"
1047 + " <InPackage> void masksEnclosingTypeVariable() {\n"
1048 + " }\n"
1049 + "\n"
1050 + " void hasSimpleNameThatWasPreviouslyMasked() {\n"
1051 + " com.squareup.tacos.InPackage inPackage = null;\n"
1052 + " }\n"
1053 + "}\n");
1054 }
1055
9871056 @Test public void originatingElementsIncludesThoseOfNestedTypes() {
9881057 Element outerElement = Mockito.mock(Element.class);
9891058 Element innerElement = Mockito.mock(Element.class);
17791848 + " }\n"
17801849 + "\n"
17811850 + " /**\n"
1782 + " * chosen by fair dice roll ;) */\n"
1851 + " * chosen by fair dice roll ;)\n"
1852 + " */\n"
17831853 + " public int getRandomQuantity() {\n"
17841854 + " return 4;\n"
17851855 + " }\n"
18381908
18391909 @Test public void nullModifiersAddition() {
18401910 try {
1841 TypeSpec.classBuilder("Taco").addModifiers((Modifier) null);
1911 TypeSpec.classBuilder("Taco").addModifiers((Modifier) null).build();
18421912 fail();
18431913 } catch(IllegalArgumentException expected) {
18441914 assertThat(expected.getMessage())
21952265
21962266 @Test public void initializersToBuilder() {
21972267 // Tests if toBuilder() contains correct static and instance initializers
2268 Element originatingElement = getElement(TypeSpecTest.class);
21982269 TypeSpec taco = TypeSpec.classBuilder("Taco")
21992270 .addField(String.class, "foo", Modifier.PRIVATE)
22002271 .addField(String.class, "FOO", Modifier.PRIVATE, Modifier.STATIC, Modifier.FINAL)
22112282 .addInitializerBlock(CodeBlock.builder()
22122283 .addStatement("foo = $S", "FOO")
22132284 .build())
2285 .addOriginatingElement(originatingElement)
2286 .alwaysQualify("com.example.AlwaysQualified")
22142287 .build();
22152288
22162289 TypeSpec recreatedTaco = taco.toBuilder().build();
22172290 assertThat(toString(taco)).isEqualTo(toString(recreatedTaco));
2291 assertThat(taco.originatingElements)
2292 .containsExactlyElementsIn(recreatedTaco.originatingElements);
2293 assertThat(taco.alwaysQualifiedNames)
2294 .containsExactlyElementsIn(recreatedTaco.alwaysQualifiedNames);
22182295
22192296 TypeSpec initializersAdded = taco.toBuilder()
22202297 .addInitializerBlock(CodeBlock.builder()
23552432 assertThat(TypeSpec.enumBuilder(className).addEnumConstant("A").build().name).isEqualTo("Example");
23562433 assertThat(TypeSpec.annotationBuilder(className).build().name).isEqualTo("Example");
23572434 }
2435
2436 @Test
2437 public void modifyAnnotations() {
2438 TypeSpec.Builder builder =
2439 TypeSpec.classBuilder("Taco")
2440 .addAnnotation(Override.class)
2441 .addAnnotation(SuppressWarnings.class);
2442
2443 builder.annotations.remove(1);
2444 assertThat(builder.build().annotations).hasSize(1);
2445 }
2446
2447 @Test
2448 public void modifyModifiers() {
2449 TypeSpec.Builder builder =
2450 TypeSpec.classBuilder("Taco").addModifiers(Modifier.PUBLIC, Modifier.FINAL);
2451
2452 builder.modifiers.remove(1);
2453 assertThat(builder.build().modifiers).containsExactly(Modifier.PUBLIC);
2454 }
2455
2456 @Test
2457 public void modifyFields() {
2458 TypeSpec.Builder builder = TypeSpec.classBuilder("Taco")
2459 .addField(int.class, "source");
2460
2461 builder.fieldSpecs.remove(0);
2462 assertThat(builder.build().fieldSpecs).isEmpty();
2463 }
2464
2465 @Test
2466 public void modifyTypeVariables() {
2467 TypeVariableName t = TypeVariableName.get("T");
2468 TypeSpec.Builder builder =
2469 TypeSpec.classBuilder("Taco")
2470 .addTypeVariable(t)
2471 .addTypeVariable(TypeVariableName.get("V"));
2472
2473 builder.typeVariables.remove(1);
2474 assertThat(builder.build().typeVariables).containsExactly(t);
2475 }
2476
2477 @Test
2478 public void modifySuperinterfaces() {
2479 TypeSpec.Builder builder = TypeSpec.classBuilder("Taco")
2480 .addSuperinterface(File.class);
2481
2482 builder.superinterfaces.clear();
2483 assertThat(builder.build().superinterfaces).isEmpty();
2484 }
2485
2486 @Test
2487 public void modifyMethods() {
2488 TypeSpec.Builder builder = TypeSpec.classBuilder("Taco")
2489 .addMethod(MethodSpec.methodBuilder("bell").build());
2490
2491 builder.methodSpecs.clear();
2492 assertThat(builder.build().methodSpecs).isEmpty();
2493 }
2494
2495 @Test
2496 public void modifyTypes() {
2497 TypeSpec.Builder builder = TypeSpec.classBuilder("Taco")
2498 .addType(TypeSpec.classBuilder("Bell").build());
2499
2500 builder.typeSpecs.clear();
2501 assertThat(builder.build().typeSpecs).isEmpty();
2502 }
2503
2504 @Test
2505 public void modifyEnumConstants() {
2506 TypeSpec constantType = TypeSpec.anonymousClassBuilder("").build();
2507 TypeSpec.Builder builder = TypeSpec.enumBuilder("Taco")
2508 .addEnumConstant("BELL", constantType)
2509 .addEnumConstant("WUT", TypeSpec.anonymousClassBuilder("").build());
2510
2511 builder.enumConstants.remove("WUT");
2512 assertThat(builder.build().enumConstants).containsExactly("BELL", constantType);
2513 }
2514
2515 @Test
2516 public void modifyOriginatingElements() {
2517 TypeSpec.Builder builder = TypeSpec.classBuilder("Taco")
2518 .addOriginatingElement(Mockito.mock(Element.class));
2519
2520 builder.originatingElements.clear();
2521 assertThat(builder.build().originatingElements).isEmpty();
2522 }
2523
2524 @Test public void javadocWithTrailingLineDoesNotAddAnother() {
2525 TypeSpec spec = TypeSpec.classBuilder("Taco")
2526 .addJavadoc("Some doc with a newline\n")
2527 .build();
2528
2529 assertThat(toString(spec)).isEqualTo(""
2530 + "package com.squareup.tacos;\n"
2531 + "\n"
2532 + "/**\n"
2533 + " * Some doc with a newline\n"
2534 + " */\n"
2535 + "class Taco {\n"
2536 + "}\n");
2537 }
2538
2539 @Test public void javadocEnsuresTrailingLine() {
2540 TypeSpec spec = TypeSpec.classBuilder("Taco")
2541 .addJavadoc("Some doc with a newline")
2542 .build();
2543
2544 assertThat(toString(spec)).isEqualTo(""
2545 + "package com.squareup.tacos;\n"
2546 + "\n"
2547 + "/**\n"
2548 + " * Some doc with a newline\n"
2549 + " */\n"
2550 + "class Taco {\n"
2551 + "}\n");
2552 }
23582553 }