aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorRoberto Lublinerman <rluble@gmail.com>2018-03-10 09:35:51 -0800
committerJesse Wilson <jesse@swank.ca>2018-03-10 12:35:51 -0500
commitd91f8e1374f7ccdb68d92c206c84864cd09c02f6 (patch)
tree45101d7ae61658b6dc553ec6eefb749eccd2f6ec
parentadced314c48865463594bf58022f509090eef7af (diff)
downloadplatform_external_javapoet-d91f8e1374f7ccdb68d92c206c84864cd09c02f6.tar.gz
platform_external_javapoet-d91f8e1374f7ccdb68d92c206c84864cd09c02f6.tar.bz2
platform_external_javapoet-d91f8e1374f7ccdb68d92c206c84864cd09c02f6.zip
Add tests for Java 8 TYPE_USE annotations on arrays (#614). (#618)
* Add tests for Java 8 TYPE_USE annotations on arrays (#614). * Implement TYPE_USE annotations semantics. * Cleanup in AnnotatedTypeTest. * Nits from review
-rw-r--r--src/main/java/com/squareup/javapoet/ArrayTypeName.java29
-rw-r--r--src/main/java/com/squareup/javapoet/ClassName.java354
-rw-r--r--src/main/java/com/squareup/javapoet/CodeWriter.java4
-rw-r--r--src/main/java/com/squareup/javapoet/JavaFile.java2
-rw-r--r--src/main/java/com/squareup/javapoet/MethodSpec.java2
-rw-r--r--src/main/java/com/squareup/javapoet/ParameterSpec.java5
-rw-r--r--src/main/java/com/squareup/javapoet/ParameterizedTypeName.java18
-rw-r--r--src/main/java/com/squareup/javapoet/TypeName.java15
-rw-r--r--src/main/java/com/squareup/javapoet/TypeVariableName.java1
-rw-r--r--src/test/java/com/squareup/javapoet/AnnotatedTypeNameTest.java140
10 files changed, 407 insertions, 163 deletions
diff --git a/src/main/java/com/squareup/javapoet/ArrayTypeName.java b/src/main/java/com/squareup/javapoet/ArrayTypeName.java
index 7c54465..219c3f3 100644
--- a/src/main/java/com/squareup/javapoet/ArrayTypeName.java
+++ b/src/main/java/com/squareup/javapoet/ArrayTypeName.java
@@ -48,9 +48,36 @@ public final class ArrayTypeName extends TypeName {
}
@Override CodeWriter emit(CodeWriter out) throws IOException {
- return out.emit("$T[]", componentType);
+ return emit(out, false);
}
+ CodeWriter emit(CodeWriter out, boolean varargs) throws IOException {
+ emitLeafType(out);
+ return emitBrackets(out, varargs);
+ }
+
+ private CodeWriter emitLeafType(CodeWriter out) throws IOException {
+ if (TypeName.asArray(componentType) != null) {
+ return TypeName.asArray(componentType).emitLeafType(out);
+ }
+ return componentType.emit(out);
+ }
+
+ private CodeWriter emitBrackets(CodeWriter out, boolean varargs) throws IOException {
+ if (isAnnotated()) {
+ out.emit(" ");
+ emitAnnotations(out);
+ }
+
+ if (TypeName.asArray(componentType) == null) {
+ // Last bracket.
+ return out.emit(varargs ? "..." : "[]");
+ }
+ out.emit("[]");
+ return TypeName.asArray(componentType) .emitBrackets(out, varargs);
+ }
+
+
/** Returns an array type whose elements are all instances of {@code componentType}. */
public static ArrayTypeName of(TypeName componentType) {
return new ArrayTypeName(componentType);
diff --git a/src/main/java/com/squareup/javapoet/ClassName.java b/src/main/java/com/squareup/javapoet/ClassName.java
index c5c065d..6c10024 100644
--- a/src/main/java/com/squareup/javapoet/ClassName.java
+++ b/src/main/java/com/squareup/javapoet/ClassName.java
@@ -17,7 +17,7 @@ package com.squareup.javapoet;
import java.io.IOException;
import java.util.ArrayList;
-import java.util.Collections;
+import java.util.Arrays;
import java.util.List;
import java.util.Map;
import javax.lang.model.SourceVersion;
@@ -32,106 +32,236 @@ import static javax.lang.model.element.NestingKind.MEMBER;
import static javax.lang.model.element.NestingKind.TOP_LEVEL;
/** A fully-qualified class name for top-level and member classes. */
-public final class ClassName extends TypeName implements Comparable<ClassName> {
- public static final ClassName OBJECT = ClassName.get(Object.class);
+public abstract class ClassName extends TypeName implements Comparable<ClassName> {
+ final String simpleName;
+ String canonicalName;
- /** From top to bottom. This will be ["java.util", "Map", "Entry"] for {@link Map.Entry}. */
- final List<String> names;
- final String canonicalName;
+ /** A fully-qualified class name for top-level classes. */
+ private static final class TopLevelClassName extends ClassName {
+ final String packageName;
- private ClassName(List<String> names) {
- this(names, new ArrayList<>());
- }
+ private TopLevelClassName(String packageName, String simpleName) {
+ this(packageName, simpleName, new ArrayList<>());
+ }
- private ClassName(List<String> names, List<AnnotationSpec> annotations) {
- super(annotations);
- for (int i = 1; i < names.size(); i++) {
- checkArgument(SourceVersion.isName(names.get(i)), "part '%s' is keyword", names.get(i));
+ private TopLevelClassName(
+ String packageName, String simpleName, List<AnnotationSpec> annotations) {
+ super(simpleName, annotations);
+ this.packageName = packageName == null ? "" : packageName;
+ this.canonicalName = isDefaultPackage(packageName)
+ ? simpleName : String.join(".", Arrays.asList(packageName, simpleName));
+ checkArgument(
+ isDefaultPackage(simpleName) || SourceVersion.isName(simpleName),
+ "part '%s' is keyword", simpleName);
+ }
+
+ @Override public TopLevelClassName annotated(List<AnnotationSpec> annotations) {
+ return new TopLevelClassName(packageName, simpleName, concatAnnotations(annotations));
+ }
+
+ @Override public TopLevelClassName withoutAnnotations() {
+ return new TopLevelClassName(packageName, simpleName);
+ }
+
+ public String packageName() {
+ return packageName;
+ }
+
+ @Override
+ public ClassName enclosingClassName() {
+ return null;
+ }
+
+ @Override
+ public TopLevelClassName topLevelClassName() {
+ return this;
+ }
+
+ @Override
+ public String reflectionName() {
+ return isDefaultPackage(packageName)
+ ? simpleName
+ : String.join(".", Arrays.asList(packageName, simpleName));
+ }
+
+ @Override
+ public List<String> simpleNames() {
+ return Arrays.asList(simpleName);
+ }
+
+ @Override
+ public ClassName peerClass(String name) {
+ return new TopLevelClassName(packageName, name);
+ }
+
+ @Override
+ ClassName prefixWithAtMostOneAnnotatedClass() {
+ return this;
+ }
+
+ @Override
+ boolean hasAnnotatedEnclosingClass() {
+ return false;
+ }
+
+ @Override
+ CodeWriter emitWithoutPrefix(CodeWriter out, ClassName unannotatedPrefix) {
+ return out;
}
- this.names = Util.immutableList(names);
- this.canonicalName = (names.get(0).isEmpty()
- ? String.join(".", names.subList(1, names.size()))
- : String.join(".", names));
}
- @Override public ClassName annotated(List<AnnotationSpec> annotations) {
- return new ClassName(names, concatAnnotations(annotations));
+ /** A fully-qualified class name for nested classes. */
+ private static final class NestedClassName extends ClassName {
+ /** From top to bottom. This will be ["java.util", "Map", "Entry"] for {@link Map.Entry}. */
+ final ClassName enclosingClassName;
+
+ private NestedClassName(ClassName enclosingClassName, String simpleName) {
+ this(enclosingClassName, simpleName, new ArrayList<>());
+ }
+
+ private NestedClassName(
+ ClassName enclosingClassName, String simpleName, List<AnnotationSpec> annotations) {
+ super(simpleName, annotations);
+ this.enclosingClassName = enclosingClassName;
+ this.canonicalName =
+ String.join(".", Arrays.asList(enclosingClassName.canonicalName, simpleName));
+ }
+
+ @Override public NestedClassName annotated(List<AnnotationSpec> annotations) {
+ return new NestedClassName(enclosingClassName, simpleName, concatAnnotations(annotations));
+ }
+
+ @Override public NestedClassName withoutAnnotations() {
+ return new NestedClassName(enclosingClassName.withoutAnnotations(), simpleName);
+ }
+
+ /** Returns the package name, like {@code "java.util"} for {@code Map.Entry}. */
+ public String packageName() {
+ return enclosingClassName.packageName();
+ }
+
+ @Override
+ public ClassName enclosingClassName() {
+ return enclosingClassName;
+ }
+
+ @Override
+ public ClassName topLevelClassName() {
+ return enclosingClassName.topLevelClassName();
+ }
+
+ @Override
+ public String reflectionName() {
+ return enclosingClassName.reflectionName() + "$" + simpleName;
+ }
+
+ @Override
+ public List<String> simpleNames() {
+ List<String> simpleNames = new ArrayList<>(enclosingClassName().simpleNames());
+ simpleNames.add(simpleName);
+ return simpleNames;
+ }
+
+ @Override
+ public ClassName peerClass(String name) {
+ return enclosingClassName.nestedClass(name);
+ }
+
+ @Override
+ ClassName prefixWithAtMostOneAnnotatedClass() {
+ if (hasAnnotatedEnclosingClass()) {
+ enclosingClassName.prefixWithAtMostOneAnnotatedClass();
+ }
+
+ return this;
+ }
+
+ @Override
+ CodeWriter emitWithoutPrefix(
+ CodeWriter out, ClassName unannotatedPrefix) throws IOException {
+
+ if (unannotatedPrefix.equals(this)) {
+ return out;
+ }
+
+ enclosingClassName.emitWithoutPrefix(out, unannotatedPrefix);
+ out.emit(".");
+ if (isAnnotated()) {
+ out.emit(" ");
+ emitAnnotations(out);
+ }
+ return out.emit(simpleName);
+ }
+
+ @Override
+ boolean hasAnnotatedEnclosingClass() {
+ return enclosingClassName.isAnnotated() || enclosingClassName.hasAnnotatedEnclosingClass();
+ }
}
- @Override public ClassName withoutAnnotations() {
- return new ClassName(names);
+ public static final ClassName OBJECT = ClassName.get(Object.class);
+
+ private ClassName(String simpleName, List<AnnotationSpec> annotations) {
+ super(annotations);
+ checkArgument(SourceVersion.isName(simpleName), "part '%s' is keyword", simpleName);
+ this.simpleName = simpleName;
}
+
/** Returns the package name, like {@code "java.util"} for {@code Map.Entry}. */
- public String packageName() {
- return names.get(0);
- }
+ public abstract String packageName();
/**
* Returns the enclosing class, like {@link Map} for {@code Map.Entry}. Returns null if this class
* is not nested in another class.
*/
- public ClassName enclosingClassName() {
- if (names.size() == 2) return null;
- return new ClassName(names.subList(0, names.size() - 1));
- }
+ public abstract ClassName enclosingClassName();
/**
* Returns the top class in this nesting group. Equivalent to chained calls to {@link
* #enclosingClassName()} until the result's enclosing class is null.
*/
- public ClassName topLevelClassName() {
- return new ClassName(names.subList(0, 2));
- }
+ public abstract ClassName topLevelClassName();
- public String reflectionName() {
- // trivial case: no nested names
- if (names.size() == 2) {
- String packageName = packageName();
- if (packageName.isEmpty()) {
- return names.get(1);
- }
- return packageName + "." + names.get(1);
- }
- // concat top level class name and nested names
- StringBuilder builder = new StringBuilder();
- builder.append(topLevelClassName());
- for (String name : simpleNames().subList(1, simpleNames().size())) {
- builder.append('$').append(name);
- }
- return builder.toString();
- }
+ /**
+ * Return the binary name of a class.
+ */
+ public abstract String reflectionName();
+
+ public abstract ClassName withoutAnnotations();
/**
* Returns a new {@link ClassName} instance for the specified {@code name} as nested inside this
* class.
*/
public ClassName nestedClass(String name) {
- checkNotNull(name, "name == null");
- List<String> result = new ArrayList<>(names.size() + 1);
- result.addAll(names);
- result.add(name);
- return new ClassName(result);
+ return new NestedClassName(this, name);
}
- public List<String> simpleNames() {
- return names.subList(1, names.size());
+ @Override
+ public ClassName annotated(List<AnnotationSpec> annotations) {
+ return (ClassName) super.annotated(annotations);
}
+ public abstract List<String> simpleNames();
+
/**
* Returns a class that shares the same enclosing package or class. If this class is enclosed by
* another class, this is equivalent to {@code enclosingClassName().nestedClass(name)}. Otherwise
* it is equivalent to {@code get(packageName(), name)}.
*/
- public ClassName peerClass(String name) {
- List<String> result = new ArrayList<>(names);
- result.set(result.size() - 1, name);
- return new ClassName(result);
- }
+ public abstract ClassName peerClass(String name);
+
+ abstract ClassName prefixWithAtMostOneAnnotatedClass();
+
+ abstract boolean hasAnnotatedEnclosingClass();
+
+ abstract CodeWriter emitWithoutPrefix(
+ CodeWriter out, ClassName unannotatedPrefix) throws IOException;
/** Returns the simple name of this class, like {@code "Entry"} for {@link Map.Entry}. */
public String simpleName() {
- return names.get(names.size() - 1);
+ return simpleName;
}
public static ClassName get(Class<?> clazz) {
@@ -139,24 +269,23 @@ public final class ClassName extends TypeName implements Comparable<ClassName> {
checkArgument(!clazz.isPrimitive(), "primitive types cannot be represented as a ClassName");
checkArgument(!void.class.equals(clazz), "'void' type cannot be represented as a ClassName");
checkArgument(!clazz.isArray(), "array types cannot be represented as a ClassName");
- List<String> names = new ArrayList<>();
- while (true) {
- String anonymousSuffix = "";
- while (clazz.isAnonymousClass()) {
- int lastDollar = clazz.getName().lastIndexOf('$');
- anonymousSuffix = clazz.getName().substring(lastDollar) + anonymousSuffix;
- clazz = clazz.getEnclosingClass();
- }
- names.add(clazz.getSimpleName() + anonymousSuffix);
- Class<?> enclosing = clazz.getEnclosingClass();
- if (enclosing == null) break;
- clazz = enclosing;
- }
- // Avoid unreliable Class.getPackage(). https://github.com/square/javapoet/issues/295
- int lastDot = clazz.getName().lastIndexOf('.');
- if (lastDot != -1) names.add(clazz.getName().substring(0, lastDot));
- Collections.reverse(names);
- return new ClassName(names);
+
+ String anonymousSuffix = "";
+ while (clazz.isAnonymousClass()) {
+ int lastDollar = clazz.getName().lastIndexOf('$');
+ anonymousSuffix = clazz.getName().substring(lastDollar) + anonymousSuffix;
+ clazz = clazz.getEnclosingClass();
+ }
+ String name = clazz.getSimpleName() + anonymousSuffix;
+
+ if (clazz.getEnclosingClass() == null) {
+ // Avoid unreliable Class.getPackage(). https://github.com/square/javapoet/issues/295
+ int lastDot = clazz.getName().lastIndexOf('.');
+ String packageName = (lastDot != -1) ? clazz.getName().substring(0, lastDot) : null;
+ return new TopLevelClassName(packageName, name);
+ }
+
+ return ClassName.get(clazz.getEnclosingClass()).nestedClass(name);
}
/**
@@ -176,17 +305,24 @@ public final class ClassName extends TypeName implements Comparable<ClassName> {
p = classNameString.indexOf('.', p) + 1;
checkArgument(p != 0, "couldn't make a guess for %s", classNameString);
}
- names.add(p != 0 ? classNameString.substring(0, p - 1) : "");
+ String packageName = p == 0 ? null : classNameString.substring(0, p - 1);
+ String[] classNames = classNameString.substring(p).split("\\.", -1);
+
+ checkArgument(classNames.length >= 1, "couldn't make a guess for %s", classNameString);
+
+ String simpleName = classNames[0];
+ checkArgument(!simpleName.isEmpty() && Character.isUpperCase(simpleName.codePointAt(0)),
+ "couldn't make a guess for %s", classNameString);
+ ClassName className = new TopLevelClassName(packageName, simpleName);
// Add the class names, like "Map" and "Entry".
- for (String part : classNameString.substring(p).split("\\.", -1)) {
+ for (String part : Arrays.asList(classNames).subList(1, classNames.length)) {
checkArgument(!part.isEmpty() && Character.isUpperCase(part.codePointAt(0)),
"couldn't make a guess for %s", classNameString);
- names.add(part);
+ className = className.nestedClass(part);
}
- checkArgument(names.size() >= 2, "couldn't make a guess for %s", classNameString);
- return new ClassName(names);
+ return className;
}
/**
@@ -194,25 +330,26 @@ public final class ClassName extends TypeName implements Comparable<ClassName> {
* {@code "java.util"} and simple names {@code "Map"}, {@code "Entry"} yields {@link Map.Entry}.
*/
public static ClassName get(String packageName, String simpleName, String... simpleNames) {
- List<String> result = new ArrayList<>();
- result.add(packageName);
- result.add(simpleName);
- Collections.addAll(result, simpleNames);
- return new ClassName(result);
+ ClassName className = new TopLevelClassName(packageName, simpleName);
+ for (String name : simpleNames) {
+ className = className.nestedClass(name);
+ }
+ return className;
}
/** Returns the class name for {@code element}. */
public static ClassName get(TypeElement element) {
checkNotNull(element, "element == null");
- List<String> names = new ArrayList<>();
- for (Element e = element; isClassOrInterface(e); e = e.getEnclosingElement()) {
- checkArgument(element.getNestingKind() == TOP_LEVEL || element.getNestingKind() == MEMBER,
- "unexpected type testing");
- names.add(e.getSimpleName().toString());
- }
- names.add(getPackage(element).getQualifiedName().toString());
- Collections.reverse(names);
- return new ClassName(names);
+ checkArgument(element.getNestingKind() == TOP_LEVEL || element.getNestingKind() == MEMBER,
+ "unexpected type nesting");
+ String simpleName = element.getSimpleName().toString();
+
+ if (isClassOrInterface(element.getEnclosingElement())) {
+ return ClassName.get((TypeElement) element.getEnclosingElement()).nestedClass(simpleName);
+ }
+
+ String packageName = getPackage(element.getEnclosingElement()).getQualifiedName().toString();
+ return new TopLevelClassName(packageName, simpleName);
}
private static boolean isClassOrInterface(Element e) {
@@ -227,10 +364,27 @@ public final class ClassName extends TypeName implements Comparable<ClassName> {
}
@Override public int compareTo(ClassName o) {
- return canonicalName.compareTo(o.canonicalName);
+ return reflectionName().compareTo(o.reflectionName());
}
@Override CodeWriter emit(CodeWriter out) throws IOException {
- return out.emitAndIndent(out.lookupName(this));
+ ClassName prefix = prefixWithAtMostOneAnnotatedClass();
+ String unqualifiedName = out.lookupName(prefix);
+ if (prefix.isAnnotated()) {
+ int dot = unqualifiedName.lastIndexOf(".");
+ out.emitAndIndent(unqualifiedName.substring(0, dot + 1));
+ if (dot != -1) {
+ out.emit(" ");
+ }
+ prefix.emitAnnotations(out);
+ out.emit(unqualifiedName.substring(dot + 1));
+ } else {
+ out.emitAndIndent(unqualifiedName);
+ }
+ return emitWithoutPrefix(out, prefix);
+ }
+
+ private static boolean isDefaultPackage(String packageName) {
+ return packageName == null || packageName.isEmpty();
}
}
diff --git a/src/main/java/com/squareup/javapoet/CodeWriter.java b/src/main/java/com/squareup/javapoet/CodeWriter.java
index 9468a08..542f434 100644
--- a/src/main/java/com/squareup/javapoet/CodeWriter.java
+++ b/src/main/java/com/squareup/javapoet/CodeWriter.java
@@ -236,10 +236,6 @@ final class CodeWriter {
case "$T":
TypeName typeName = (TypeName) codeBlock.args.get(a++);
- if (typeName.isAnnotated()) {
- typeName.emitAnnotations(this);
- typeName = typeName.withoutAnnotations();
- }
// defer "typeName.emit(this)" if next format part will be handled by the default case
if (typeName instanceof ClassName && partIterator.hasNext()) {
if (!codeBlock.formatParts.get(partIterator.nextIndex()).startsWith("$")) {
diff --git a/src/main/java/com/squareup/javapoet/JavaFile.java b/src/main/java/com/squareup/javapoet/JavaFile.java
index b6b5a75..e7662dd 100644
--- a/src/main/java/com/squareup/javapoet/JavaFile.java
+++ b/src/main/java/com/squareup/javapoet/JavaFile.java
@@ -145,7 +145,7 @@ public final class JavaFile {
int importedTypesCount = 0;
for (ClassName className : new TreeSet<>(codeWriter.importedTypes().values())) {
if (skipJavaLangImports && className.packageName().equals("java.lang")) continue;
- codeWriter.emit("import $L;\n", className);
+ codeWriter.emit("import $L;\n", className.withoutAnnotations());
importedTypesCount++;
}
diff --git a/src/main/java/com/squareup/javapoet/MethodSpec.java b/src/main/java/com/squareup/javapoet/MethodSpec.java
index 453d64d..a2c7c43 100644
--- a/src/main/java/com/squareup/javapoet/MethodSpec.java
+++ b/src/main/java/com/squareup/javapoet/MethodSpec.java
@@ -77,7 +77,7 @@ public final class MethodSpec {
private boolean lastParameterIsArray(List<ParameterSpec> parameters) {
return !parameters.isEmpty()
- && TypeName.arrayComponent(parameters.get(parameters.size() - 1).type) != null;
+ && TypeName.asArray((parameters.get(parameters.size() - 1).type)) != null;
}
void emit(CodeWriter codeWriter, String enclosingName, Set<Modifier> implicitModifiers)
diff --git a/src/main/java/com/squareup/javapoet/ParameterSpec.java b/src/main/java/com/squareup/javapoet/ParameterSpec.java
index 4f52872..63da3f2 100644
--- a/src/main/java/com/squareup/javapoet/ParameterSpec.java
+++ b/src/main/java/com/squareup/javapoet/ParameterSpec.java
@@ -51,10 +51,11 @@ public final class ParameterSpec {
codeWriter.emitAnnotations(annotations, true);
codeWriter.emitModifiers(modifiers);
if (varargs) {
- codeWriter.emit("$T... $L", TypeName.arrayComponent(type), name);
+ TypeName.asArray(type).emit(codeWriter, true);
} else {
- codeWriter.emit("$T $L", type, name);
+ type.emit(codeWriter);
}
+ codeWriter.emit(" $L", name);
}
@Override public boolean equals(Object o) {
diff --git a/src/main/java/com/squareup/javapoet/ParameterizedTypeName.java b/src/main/java/com/squareup/javapoet/ParameterizedTypeName.java
index d46aba8..3a8bf62 100644
--- a/src/main/java/com/squareup/javapoet/ParameterizedTypeName.java
+++ b/src/main/java/com/squareup/javapoet/ParameterizedTypeName.java
@@ -41,7 +41,7 @@ public final class ParameterizedTypeName extends TypeName {
private ParameterizedTypeName(ParameterizedTypeName enclosingType, ClassName rawType,
List<TypeName> typeArguments, List<AnnotationSpec> annotations) {
super(annotations);
- this.rawType = checkNotNull(rawType, "rawType == null");
+ this.rawType = checkNotNull(rawType, "rawType == null").annotated(annotations);
this.enclosingType = enclosingType;
this.typeArguments = Util.immutableList(typeArguments);
@@ -58,17 +58,22 @@ public final class ParameterizedTypeName extends TypeName {
enclosingType, rawType, typeArguments, concatAnnotations(annotations));
}
- @Override public TypeName withoutAnnotations() {
- return new ParameterizedTypeName(enclosingType, rawType, typeArguments, new ArrayList<>());
+ @Override
+ public TypeName withoutAnnotations() {
+ return new ParameterizedTypeName(
+ enclosingType, rawType.withoutAnnotations(), typeArguments, new ArrayList<>());
}
@Override CodeWriter emit(CodeWriter out) throws IOException {
if (enclosingType != null) {
- enclosingType.emitAnnotations(out);
enclosingType.emit(out);
- out.emit("." + rawType.simpleName());
+ out.emit(".");
+ if (isAnnotated()) {
+ out.emit(" ");
+ emitAnnotations(out);
+ }
+ out.emit(rawType.simpleName());
} else {
- rawType.emitAnnotations(out);
rawType.emit(out);
}
if (!typeArguments.isEmpty()) {
@@ -76,7 +81,6 @@ public final class ParameterizedTypeName extends TypeName {
boolean firstParameter = true;
for (TypeName parameter : typeArguments) {
if (!firstParameter) out.emitAndIndent(", ");
- parameter.emitAnnotations(out);
parameter.emit(out);
firstParameter = false;
}
diff --git a/src/main/java/com/squareup/javapoet/TypeName.java b/src/main/java/com/squareup/javapoet/TypeName.java
index e09d785..38877f7 100644
--- a/src/main/java/com/squareup/javapoet/TypeName.java
+++ b/src/main/java/com/squareup/javapoet/TypeName.java
@@ -209,7 +209,6 @@ public class TypeName {
try {
StringBuilder resultBuilder = new StringBuilder();
CodeWriter codeWriter = new CodeWriter(resultBuilder);
- emitAnnotations(codeWriter);
emit(codeWriter);
result = resultBuilder.toString();
cachedString = result;
@@ -222,6 +221,11 @@ public class TypeName {
CodeWriter emit(CodeWriter out) throws IOException {
if (keyword == null) throw new AssertionError();
+
+ if (isAnnotated()) {
+ out.emit("");
+ emitAnnotations(out);
+ }
return out.emitAndIndent(keyword);
}
@@ -233,6 +237,7 @@ public class TypeName {
return out;
}
+
/** Returns a type name equivalent to {@code mirror}. */
public static TypeName get(TypeMirror mirror) {
return get(mirror, new LinkedHashMap<>());
@@ -369,4 +374,12 @@ public class TypeName {
? ((ArrayTypeName) type).componentType
: null;
}
+
+ /** Returns {@code type} as an array, or null if {@code type} is not an array. */
+ static ArrayTypeName asArray(TypeName type) {
+ return type instanceof ArrayTypeName
+ ? ((ArrayTypeName) type)
+ : null;
+ }
+
}
diff --git a/src/main/java/com/squareup/javapoet/TypeVariableName.java b/src/main/java/com/squareup/javapoet/TypeVariableName.java
index e0e54fb..54c2fa5 100644
--- a/src/main/java/com/squareup/javapoet/TypeVariableName.java
+++ b/src/main/java/com/squareup/javapoet/TypeVariableName.java
@@ -80,6 +80,7 @@ public final class TypeVariableName extends TypeName {
}
@Override CodeWriter emit(CodeWriter out) throws IOException {
+ emitAnnotations(out);
return out.emitAndIndent(name);
}
diff --git a/src/test/java/com/squareup/javapoet/AnnotatedTypeNameTest.java b/src/test/java/com/squareup/javapoet/AnnotatedTypeNameTest.java
index 18b7f7e..7261568 100644
--- a/src/test/java/com/squareup/javapoet/AnnotatedTypeNameTest.java
+++ b/src/test/java/com/squareup/javapoet/AnnotatedTypeNameTest.java
@@ -15,6 +15,7 @@
*/
package com.squareup.javapoet;
+import static com.google.common.truth.Truth.assertThat;
import static org.junit.Assert.assertEquals;
import static org.junit.Assert.assertFalse;
import static org.junit.Assert.assertNotEquals;
@@ -24,16 +25,23 @@ import java.lang.annotation.ElementType;
import java.lang.annotation.Target;
import java.util.List;
import java.util.Map;
-import org.junit.Ignore;
import org.junit.Test;
public class AnnotatedTypeNameTest {
private final static String NN = NeverNull.class.getCanonicalName();
private final AnnotationSpec NEVER_NULL = AnnotationSpec.builder(NeverNull.class).build();
+ private final static String TUA = TypeUseAnnotation.class.getCanonicalName();
+ private final AnnotationSpec TYPE_USE_ANNOTATION =
+ AnnotationSpec.builder(TypeUseAnnotation.class).build();
+ @Target(ElementType.TYPE_USE)
public @interface NeverNull {}
+ @Target(ElementType.TYPE_USE)
+ public @interface TypeUseAnnotation {}
+
+
@Test(expected=NullPointerException.class) public void nullAnnotationArray() {
TypeName.BOOLEAN.annotated((AnnotationSpec[]) null);
}
@@ -46,55 +54,49 @@ public class AnnotatedTypeNameTest {
TypeName simpleString = TypeName.get(String.class);
assertFalse(simpleString.isAnnotated());
assertEquals(simpleString, TypeName.get(String.class));
+
TypeName annotated = simpleString.annotated(NEVER_NULL);
assertTrue(annotated.isAnnotated());
assertEquals(annotated, annotated.annotated());
}
@Test public void annotatedType() {
- String expected = "@" + NN + " java.lang.String";
TypeName type = TypeName.get(String.class);
- String actual = type.annotated(NEVER_NULL).toString();
- assertEquals(expected, actual);
+ TypeName actual = type.annotated(TYPE_USE_ANNOTATION);
+ assertThat(actual.toString()).isEqualTo("java.lang. @" + TUA + " String");
}
@Test public void annotatedTwice() {
- String expected = "@" + NN + " @java.lang.Override java.lang.String";
TypeName type = TypeName.get(String.class);
- String actual =
+ TypeName actual =
type.annotated(NEVER_NULL)
- .annotated(AnnotationSpec.builder(Override.class).build())
- .toString();
- assertEquals(expected, actual);
+ .annotated(TYPE_USE_ANNOTATION);
+ assertThat(actual.toString())
+ .isEqualTo("java.lang. @" + NN + " @" + TUA + " String");
}
@Test public void annotatedParameterizedType() {
- String expected = "@" + NN + " java.util.List<java.lang.String>";
TypeName type = ParameterizedTypeName.get(List.class, String.class);
- String actual = type.annotated(NEVER_NULL).toString();
- assertEquals(expected, actual);
+ TypeName actual = type.annotated(TYPE_USE_ANNOTATION);
+ assertThat(actual.toString()).isEqualTo("java.util. @" + TUA + " List<java.lang.String>");
}
@Test public void annotatedArgumentOfParameterizedType() {
- String expected = "java.util.List<@" + NN + " java.lang.String>";
- TypeName type = TypeName.get(String.class).annotated(NEVER_NULL);
- ClassName list = ClassName.get(List.class);
- String actual = ParameterizedTypeName.get(list, type).toString();
- assertEquals(expected, actual);
+ TypeName type = TypeName.get(String.class).annotated(TYPE_USE_ANNOTATION);
+ TypeName actual = ParameterizedTypeName.get(ClassName.get(List.class), type);
+ assertThat(actual.toString()).isEqualTo("java.util.List<java.lang. @" + TUA + " String>");
}
@Test public void annotatedWildcardTypeNameWithSuper() {
- String expected = "? super @" + NN + " java.lang.String";
- TypeName type = TypeName.get(String.class).annotated(NEVER_NULL);
- String actual = WildcardTypeName.supertypeOf(type).toString();
- assertEquals(expected, actual);
+ TypeName type = TypeName.get(String.class).annotated(TYPE_USE_ANNOTATION);
+ TypeName actual = WildcardTypeName.supertypeOf(type);
+ assertThat(actual.toString()).isEqualTo("? super java.lang. @" + TUA + " String");
}
@Test public void annotatedWildcardTypeNameWithExtends() {
- String expected = "? extends @" + NN + " java.lang.String";
- TypeName type = TypeName.get(String.class).annotated(NEVER_NULL);
- String actual = WildcardTypeName.subtypeOf(type).toString();
- assertEquals(expected, actual);
+ TypeName type = TypeName.get(String.class).annotated(TYPE_USE_ANNOTATION);
+ TypeName actual = WildcardTypeName.subtypeOf(type);
+ assertThat(actual.toString()).isEqualTo("? extends java.lang. @" + TUA + " String");
}
@Test public void annotatedEquivalence() {
@@ -109,34 +111,80 @@ public class AnnotatedTypeNameTest {
private void annotatedEquivalence(TypeName type) {
assertFalse(type.isAnnotated());
assertEquals(type, type);
- assertEquals(type.annotated(NEVER_NULL), type.annotated(NEVER_NULL));
- assertNotEquals(type, type.annotated(NEVER_NULL));
+ assertEquals(type.annotated(TYPE_USE_ANNOTATION), type.annotated(TYPE_USE_ANNOTATION));
+ assertNotEquals(type, type.annotated(TYPE_USE_ANNOTATION));
assertEquals(type.hashCode(), type.hashCode());
- assertEquals(type.annotated(NEVER_NULL).hashCode(), type.annotated(NEVER_NULL).hashCode());
- assertNotEquals(type.hashCode(), type.annotated(NEVER_NULL).hashCode());
+ assertEquals(type.annotated(TYPE_USE_ANNOTATION).hashCode(),
+ type.annotated(TYPE_USE_ANNOTATION).hashCode());
+ assertNotEquals(type.hashCode(), type.annotated(TYPE_USE_ANNOTATION).hashCode());
}
// https://github.com/square/javapoet/issues/431
- @Target(ElementType.TYPE_USE)
- public @interface TypeUseAnnotation {}
-
- // https://github.com/square/javapoet/issues/431
- @Ignore @Test public void annotatedNestedType() {
- String expected = "java.util.Map.@" + TypeUseAnnotation.class.getCanonicalName() + " Entry";
- AnnotationSpec typeUseAnnotation = AnnotationSpec.builder(TypeUseAnnotation.class).build();
- TypeName type = TypeName.get(Map.Entry.class).annotated(typeUseAnnotation);
- String actual = type.toString();
- assertEquals(expected, actual);
+ @Test public void annotatedNestedType() {
+ TypeName type = TypeName.get(Map.Entry.class).annotated(TYPE_USE_ANNOTATION);
+ assertThat(type.toString()).isEqualTo("java.util.Map. @" + TUA + " Entry");
}
// https://github.com/square/javapoet/issues/431
- @Ignore @Test public void annotatedNestedParameterizedType() {
- String expected = "java.util.Map.@" + TypeUseAnnotation.class.getCanonicalName()
- + " Entry<java.lang.Byte, java.lang.Byte>";
- AnnotationSpec typeUseAnnotation = AnnotationSpec.builder(TypeUseAnnotation.class).build();
+ @Test public void annotatedNestedParameterizedType() {
TypeName type = ParameterizedTypeName.get(Map.Entry.class, Byte.class, Byte.class)
- .annotated(typeUseAnnotation);
- String actual = type.toString();
- assertEquals(expected, actual);
+ .annotated(TYPE_USE_ANNOTATION);
+ assertThat(type.toString())
+ .isEqualTo("java.util.Map. @" + TUA + " Entry<java.lang.Byte, java.lang.Byte>");
+ }
+
+ // https://github.com/square/javapoet/issues/614
+ @Test public void annotatedArrayType() {
+ TypeName type = ArrayTypeName.of(ClassName.get(Object.class)).annotated(TYPE_USE_ANNOTATION);
+ assertThat(type.toString()).isEqualTo("java.lang.Object @" + TUA + " []");
+ }
+
+ @Test public void annotatedArrayElementType() {
+ TypeName type = ArrayTypeName.of(ClassName.get(Object.class).annotated(TYPE_USE_ANNOTATION));
+ assertThat(type.toString()).isEqualTo("java.lang. @" + TUA + " Object[]");
+ }
+
+ // https://github.com/square/javapoet/issues/614
+ @Test public void annotatedOuterMultidimensionalArrayType() {
+ TypeName type = ArrayTypeName.of(ArrayTypeName.of(ClassName.get(Object.class)))
+ .annotated(TYPE_USE_ANNOTATION);
+ assertThat(type.toString()).isEqualTo("java.lang.Object @" + TUA + " [][]");
+ }
+
+ // https://github.com/square/javapoet/issues/614
+ @Test public void annotatedInnerMultidimensionalArrayType() {
+ TypeName type = ArrayTypeName.of(ArrayTypeName.of(ClassName.get(Object.class))
+ .annotated(TYPE_USE_ANNOTATION));
+ assertThat(type.toString()).isEqualTo("java.lang.Object[] @" + TUA + " []");
+ }
+
+ // https://github.com/square/javapoet/issues/614
+ @Test public void annotatedArrayTypeVarargsParameter() {
+ TypeName type = ArrayTypeName.of(ArrayTypeName.of(ClassName.get(Object.class)))
+ .annotated(TYPE_USE_ANNOTATION);
+ MethodSpec varargsMethod = MethodSpec.methodBuilder("m")
+ .addParameter(
+ ParameterSpec.builder(type, "p")
+ .build())
+ .varargs()
+ .build();
+ assertThat(varargsMethod.toString()).isEqualTo(""
+ + "void m(java.lang.Object @" + TUA + " []... p) {\n"
+ + "}\n");
+ }
+
+ // https://github.com/square/javapoet/issues/614
+ @Test public void annotatedArrayTypeInVarargsParameter() {
+ TypeName type = ArrayTypeName.of(ArrayTypeName.of(ClassName.get(Object.class))
+ .annotated(TYPE_USE_ANNOTATION));
+ MethodSpec varargsMethod = MethodSpec.methodBuilder("m")
+ .addParameter(
+ ParameterSpec.builder(type, "p")
+ .build())
+ .varargs()
+ .build();
+ assertThat(varargsMethod.toString()).isEqualTo(""
+ + "void m(java.lang.Object[] @" + TUA + " ... p) {\n"
+ + "}\n");
}
}