/* * Copyright (C) 2015 Square, Inc. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package com.squareup.javapoet; import java.io.File; import com.google.testing.compile.CompilationRule; import java.util.Collections; import java.util.Date; import java.util.List; import java.util.Map; import java.util.Optional; import java.util.concurrent.TimeUnit; import java.util.regex.Pattern; import javax.lang.model.element.Modifier; import javax.lang.model.element.TypeElement; import org.junit.Ignore; import org.junit.Rule; import org.junit.Test; import org.junit.runner.RunWith; import org.junit.runners.JUnit4; import static com.google.common.truth.Truth.assertThat; @RunWith(JUnit4.class) public final class JavaFileTest { @Rule public final CompilationRule compilation = new CompilationRule(); private TypeElement getElement(Class<?> clazz) { return compilation.getElements().getTypeElement(clazz.getCanonicalName()); } @Test public void importStaticReadmeExample() { ClassName hoverboard = ClassName.get("com.mattel", "Hoverboard"); ClassName namedBoards = ClassName.get("com.mattel", "Hoverboard", "Boards"); ClassName list = ClassName.get("java.util", "List"); ClassName arrayList = ClassName.get("java.util", "ArrayList"); TypeName listOfHoverboards = ParameterizedTypeName.get(list, hoverboard); MethodSpec beyond = MethodSpec.methodBuilder("beyond") .returns(listOfHoverboards) .addStatement("$T result = new $T<>()", listOfHoverboards, arrayList) .addStatement("result.add($T.createNimbus(2000))", hoverboard) .addStatement("result.add($T.createNimbus(\"2001\"))", hoverboard) .addStatement("result.add($T.createNimbus($T.THUNDERBOLT))", hoverboard, namedBoards) .addStatement("$T.sort(result)", Collections.class) .addStatement("return result.isEmpty() ? $T.emptyList() : result", Collections.class) .build(); TypeSpec hello = TypeSpec.classBuilder("HelloWorld") .addMethod(beyond) .build(); JavaFile example = JavaFile.builder("com.example.helloworld", hello) .addStaticImport(hoverboard, "createNimbus") .addStaticImport(namedBoards, "*") .addStaticImport(Collections.class, "*") .build(); assertThat(example.toString()).isEqualTo("" + "package com.example.helloworld;\n" + "\n" + "import static com.mattel.Hoverboard.Boards.*;\n" + "import static com.mattel.Hoverboard.createNimbus;\n" + "import static java.util.Collections.*;\n" + "\n" + "import com.mattel.Hoverboard;\n" + "import java.util.ArrayList;\n" + "import java.util.List;\n" + "\n" + "class HelloWorld {\n" + " List<Hoverboard> beyond() {\n" + " List<Hoverboard> result = new ArrayList<>();\n" + " result.add(createNimbus(2000));\n" + " result.add(createNimbus(\"2001\"));\n" + " result.add(createNimbus(THUNDERBOLT));\n" + " sort(result);\n" + " return result.isEmpty() ? emptyList() : result;\n" + " }\n" + "}\n"); } @Test public void importStaticForCrazyFormatsWorks() { MethodSpec method = MethodSpec.methodBuilder("method").build(); JavaFile.builder("com.squareup.tacos", TypeSpec.classBuilder("Taco") .addStaticBlock(CodeBlock.builder() .addStatement("$T", Runtime.class) .addStatement("$T.a()", Runtime.class) .addStatement("$T.X", Runtime.class) .addStatement("$T$T", Runtime.class, Runtime.class) .addStatement("$T.$T", Runtime.class, Runtime.class) .addStatement("$1T$1T", Runtime.class) .addStatement("$1T$2L$1T", Runtime.class, "?") .addStatement("$1T$2L$2S$1T", Runtime.class, "?") .addStatement("$1T$2L$2S$1T$3N$1T", Runtime.class, "?", method) .addStatement("$T$L", Runtime.class, "?") .addStatement("$T$S", Runtime.class, "?") .addStatement("$T$N", Runtime.class, method) .build()) .build()) .addStaticImport(Runtime.class, "*") .build() .toString(); // don't look at the generated code... } @Test public void importStaticMixed() { JavaFile source = JavaFile.builder("com.squareup.tacos", TypeSpec.classBuilder("Taco") .addStaticBlock(CodeBlock.builder() .addStatement("assert $1T.valueOf(\"BLOCKED\") == $1T.BLOCKED", Thread.State.class) .addStatement("$T.gc()", System.class) .addStatement("$1T.out.println($1T.nanoTime())", System.class) .build()) .addMethod(MethodSpec.constructorBuilder() .addParameter(Thread.State[].class, "states") .varargs(true) .build()) .build()) .addStaticImport(Thread.State.BLOCKED) .addStaticImport(System.class, "*") .addStaticImport(Thread.State.class, "valueOf") .build(); assertThat(source.toString()).isEqualTo("" + "package com.squareup.tacos;\n" + "\n" + "import static java.lang.System.*;\n" + "import static java.lang.Thread.State.BLOCKED;\n" + "import static java.lang.Thread.State.valueOf;\n" + "\n" + "import java.lang.Thread;\n" + "\n" + "class Taco {\n" + " static {\n" + " assert valueOf(\"BLOCKED\") == BLOCKED;\n" + " gc();\n" + " out.println(nanoTime());\n" + " }\n" + "\n" + " Taco(Thread.State... states) {\n" + " }\n" + "}\n"); } @Ignore("addStaticImport doesn't support members with $L") @Test public void importStaticDynamic() { JavaFile source = JavaFile.builder("com.squareup.tacos", TypeSpec.classBuilder("Taco") .addMethod(MethodSpec.methodBuilder("main") .addStatement("$T.$L.println($S)", System.class, "out", "hello") .build()) .build()) .addStaticImport(System.class, "out") .build(); assertThat(source.toString()).isEqualTo("" + "package com.squareup.tacos;\n" + "\n" + "import static java.lang.System.out;\n" + "\n" + "class Taco {\n" + " void main() {\n" + " out.println(\"hello\");\n" + " }\n" + "}\n"); } @Test public void importStaticNone() { assertThat(JavaFile.builder("readme", importStaticTypeSpec("Util")) .build().toString()).isEqualTo("" + "package readme;\n" + "\n" + "import java.lang.System;\n" + "import java.util.concurrent.TimeUnit;\n" + "\n" + "class Util {\n" + " public static long minutesToSeconds(long minutes) {\n" + " System.gc();\n" + " return TimeUnit.SECONDS.convert(minutes, TimeUnit.MINUTES);\n" + " }\n" + "}\n"); } @Test public void importStaticOnce() { assertThat(JavaFile.builder("readme", importStaticTypeSpec("Util")) .addStaticImport(TimeUnit.SECONDS) .build().toString()).isEqualTo("" + "package readme;\n" + "\n" + "import static java.util.concurrent.TimeUnit.SECONDS;\n" + "\n" + "import java.lang.System;\n" + "import java.util.concurrent.TimeUnit;\n" + "\n" + "class Util {\n" + " public static long minutesToSeconds(long minutes) {\n" + " System.gc();\n" + " return SECONDS.convert(minutes, TimeUnit.MINUTES);\n" + " }\n" + "}\n"); } @Test public void importStaticTwice() { assertThat(JavaFile.builder("readme", importStaticTypeSpec("Util")) .addStaticImport(TimeUnit.SECONDS) .addStaticImport(TimeUnit.MINUTES) .build().toString()).isEqualTo("" + "package readme;\n" + "\n" + "import static java.util.concurrent.TimeUnit.MINUTES;\n" + "import static java.util.concurrent.TimeUnit.SECONDS;\n" + "\n" + "import java.lang.System;\n" + "\n" + "class Util {\n" + " public static long minutesToSeconds(long minutes) {\n" + " System.gc();\n" + " return SECONDS.convert(minutes, MINUTES);\n" + " }\n" + "}\n"); } @Test public void importStaticUsingWildcards() { assertThat(JavaFile.builder("readme", importStaticTypeSpec("Util")) .addStaticImport(TimeUnit.class, "*") .addStaticImport(System.class, "*") .build().toString()).isEqualTo("" + "package readme;\n" + "\n" + "import static java.lang.System.*;\n" + "import static java.util.concurrent.TimeUnit.*;\n" + "\n" + "class Util {\n" + " public static long minutesToSeconds(long minutes) {\n" + " gc();\n" + " return SECONDS.convert(minutes, MINUTES);\n" + " }\n" + "}\n"); } private TypeSpec importStaticTypeSpec(String name) { MethodSpec method = MethodSpec.methodBuilder("minutesToSeconds") .addModifiers(Modifier.PUBLIC, Modifier.STATIC) .returns(long.class) .addParameter(long.class, "minutes") .addStatement("$T.gc()", System.class) .addStatement("return $1T.SECONDS.convert(minutes, $1T.MINUTES)", TimeUnit.class) .build(); return TypeSpec.classBuilder(name).addMethod(method).build(); } @Test public void noImports() throws Exception { String source = JavaFile.builder("com.squareup.tacos", TypeSpec.classBuilder("Taco").build()) .build() .toString(); assertThat(source).isEqualTo("" + "package com.squareup.tacos;\n" + "\n" + "class Taco {\n" + "}\n"); } @Test public void singleImport() throws Exception { String source = JavaFile.builder("com.squareup.tacos", TypeSpec.classBuilder("Taco") .addField(Date.class, "madeFreshDate") .build()) .build() .toString(); assertThat(source).isEqualTo("" + "package com.squareup.tacos;\n" + "\n" + "import java.util.Date;\n" + "\n" + "class Taco {\n" + " Date madeFreshDate;\n" + "}\n"); } @Test public void conflictingImports() throws Exception { String source = JavaFile.builder("com.squareup.tacos", TypeSpec.classBuilder("Taco") .addField(Date.class, "madeFreshDate") .addField(ClassName.get("java.sql", "Date"), "madeFreshDatabaseDate") .build()) .build() .toString(); assertThat(source).isEqualTo("" + "package com.squareup.tacos;\n" + "\n" + "import java.util.Date;\n" + "\n" + "class Taco {\n" + " Date madeFreshDate;\n" + "\n" + " java.sql.Date madeFreshDatabaseDate;\n" + "}\n"); } @Test public void annotatedTypeParam() throws Exception { String source = JavaFile.builder("com.squareup.tacos", TypeSpec.classBuilder("Taco") .addField(ParameterizedTypeName.get(ClassName.get(List.class), ClassName.get("com.squareup.meat", "Chorizo") .annotated(AnnotationSpec.builder(ClassName.get("com.squareup.tacos", "Spicy")) .build())), "chorizo") .build()) .build() .toString(); assertThat(source).isEqualTo("" + "package com.squareup.tacos;\n" + "\n" + "import com.squareup.meat.Chorizo;\n" + "import java.util.List;\n" + "\n" + "class Taco {\n" + " List<@Spicy Chorizo> chorizo;\n" + "}\n"); } @Test public void skipJavaLangImportsWithConflictingClassLast() throws Exception { // Whatever is used first wins! In this case the Float in java.lang is imported. String source = JavaFile.builder("com.squareup.tacos", TypeSpec.classBuilder("Taco") .addField(ClassName.get("java.lang", "Float"), "litres") .addField(ClassName.get("com.squareup.soda", "Float"), "beverage") .build()) .skipJavaLangImports(true) .build() .toString(); assertThat(source).isEqualTo("" + "package com.squareup.tacos;\n" + "\n" + "class Taco {\n" + " Float litres;\n" + "\n" + " com.squareup.soda.Float beverage;\n" // Second 'Float' is fully qualified. + "}\n"); } @Test public void skipJavaLangImportsWithConflictingClassFirst() throws Exception { // Whatever is used first wins! In this case the Float in com.squareup.soda is imported. String source = JavaFile.builder("com.squareup.tacos", TypeSpec.classBuilder("Taco") .addField(ClassName.get("com.squareup.soda", "Float"), "beverage") .addField(ClassName.get("java.lang", "Float"), "litres") .build()) .skipJavaLangImports(true) .build() .toString(); assertThat(source).isEqualTo("" + "package com.squareup.tacos;\n" + "\n" + "import com.squareup.soda.Float;\n" + "\n" + "class Taco {\n" + " Float beverage;\n" + "\n" + " java.lang.Float litres;\n" // Second 'Float' is fully qualified. + "}\n"); } @Test public void conflictingParentName() throws Exception { String source = JavaFile.builder("com.squareup.tacos", TypeSpec.classBuilder("A") .addType(TypeSpec.classBuilder("B") .addType(TypeSpec.classBuilder("Twin").build()) .addType(TypeSpec.classBuilder("C") .addField(ClassName.get("com.squareup.tacos", "A", "Twin", "D"), "d") .build()) .build()) .addType(TypeSpec.classBuilder("Twin") .addType(TypeSpec.classBuilder("D") .build()) .build()) .build()) .build() .toString(); assertThat(source).isEqualTo("" + "package com.squareup.tacos;\n" + "\n" + "class A {\n" + " class B {\n" + " class Twin {\n" + " }\n" + "\n" + " class C {\n" + " A.Twin.D d;\n" + " }\n" + " }\n" + "\n" + " class Twin {\n" + " class D {\n" + " }\n" + " }\n" + "}\n"); } @Test public void conflictingChildName() throws Exception { String source = JavaFile.builder("com.squareup.tacos", TypeSpec.classBuilder("A") .addType(TypeSpec.classBuilder("B") .addType(TypeSpec.classBuilder("C") .addField(ClassName.get("com.squareup.tacos", "A", "Twin", "D"), "d") .addType(TypeSpec.classBuilder("Twin").build()) .build()) .build()) .addType(TypeSpec.classBuilder("Twin") .addType(TypeSpec.classBuilder("D") .build()) .build()) .build()) .build() .toString(); assertThat(source).isEqualTo("" + "package com.squareup.tacos;\n" + "\n" + "class A {\n" + " class B {\n" + " class C {\n" + " A.Twin.D d;\n" + "\n" + " class Twin {\n" + " }\n" + " }\n" + " }\n" + "\n" + " class Twin {\n" + " class D {\n" + " }\n" + " }\n" + "}\n"); } @Test public void conflictingNameOutOfScope() throws Exception { String source = JavaFile.builder("com.squareup.tacos", TypeSpec.classBuilder("A") .addType(TypeSpec.classBuilder("B") .addType(TypeSpec.classBuilder("C") .addField(ClassName.get("com.squareup.tacos", "A", "Twin", "D"), "d") .addType(TypeSpec.classBuilder("Nested") .addType(TypeSpec.classBuilder("Twin").build()) .build()) .build()) .build()) .addType(TypeSpec.classBuilder("Twin") .addType(TypeSpec.classBuilder("D") .build()) .build()) .build()) .build() .toString(); assertThat(source).isEqualTo("" + "package com.squareup.tacos;\n" + "\n" + "class A {\n" + " class B {\n" + " class C {\n" + " Twin.D d;\n" + "\n" + " class Nested {\n" + " class Twin {\n" + " }\n" + " }\n" + " }\n" + " }\n" + "\n" + " class Twin {\n" + " class D {\n" + " }\n" + " }\n" + "}\n"); } @Test public void nestedClassAndSuperclassShareName() throws Exception { String source = JavaFile.builder("com.squareup.tacos", TypeSpec.classBuilder("Taco") .superclass(ClassName.get("com.squareup.wire", "Message")) .addType(TypeSpec.classBuilder("Builder") .superclass(ClassName.get("com.squareup.wire", "Message", "Builder")) .build()) .build()) .build() .toString(); assertThat(source).isEqualTo("" + "package com.squareup.tacos;\n" + "\n" + "import com.squareup.wire.Message;\n" + "\n" + "class Taco extends Message {\n" + " class Builder extends Message.Builder {\n" + " }\n" + "}\n"); } @Test public void classAndSuperclassShareName() throws Exception { String source = JavaFile.builder("com.squareup.tacos", TypeSpec.classBuilder("Taco") .superclass(ClassName.get("com.taco.bell", "Taco")) .build()) .build() .toString(); assertThat(source).isEqualTo("" + "package com.squareup.tacos;\n" + "\n" + "class Taco extends com.taco.bell.Taco {\n" + "}\n"); } @Test public void conflictingAnnotation() throws Exception { String source = JavaFile.builder("com.squareup.tacos", TypeSpec.classBuilder("Taco") .addAnnotation(ClassName.get("com.taco.bell", "Taco")) .build()) .build() .toString(); assertThat(source).isEqualTo("" + "package com.squareup.tacos;\n" + "\n" + "@com.taco.bell.Taco\n" + "class Taco {\n" + "}\n"); } @Test public void conflictingAnnotationReferencedClass() throws Exception { String source = JavaFile.builder("com.squareup.tacos", TypeSpec.classBuilder("Taco") .addAnnotation(AnnotationSpec.builder(ClassName.get("com.squareup.tacos", "MyAnno")) .addMember("value", "$T.class", ClassName.get("com.taco.bell", "Taco")) .build()) .build()) .build() .toString(); assertThat(source).isEqualTo("" + "package com.squareup.tacos;\n" + "\n" + "@MyAnno(com.taco.bell.Taco.class)\n" + "class Taco {\n" + "}\n"); } @Test public void conflictingTypeVariableBound() throws Exception { String source = JavaFile.builder("com.squareup.tacos", TypeSpec.classBuilder("Taco") .addTypeVariable( TypeVariableName.get("T", ClassName.get("com.taco.bell", "Taco"))) .build()) .build() .toString(); assertThat(source).isEqualTo("" + "package com.squareup.tacos;\n" + "\n" + "class Taco<T extends com.taco.bell.Taco> {\n" + "}\n"); } @Test public void superclassReferencesSelf() throws Exception { String source = JavaFile.builder("com.squareup.tacos", TypeSpec.classBuilder("Taco") .superclass(ParameterizedTypeName.get( ClassName.get(Comparable.class), ClassName.get("com.squareup.tacos", "Taco"))) .build()) .build() .toString(); assertThat(source).isEqualTo("" + "package com.squareup.tacos;\n" + "\n" + "import java.lang.Comparable;\n" + "\n" + "class Taco extends Comparable<Taco> {\n" + "}\n"); } /** https://github.com/square/javapoet/issues/366 */ @Test public void annotationIsNestedClass() throws Exception { String source = JavaFile.builder("com.squareup.tacos", TypeSpec.classBuilder("TestComponent") .addAnnotation(ClassName.get("dagger", "Component")) .addType(TypeSpec.classBuilder("Builder") .addAnnotation(ClassName.get("dagger", "Component", "Builder")) .build()) .build()) .build() .toString(); assertThat(source).isEqualTo("" + "package com.squareup.tacos;\n" + "\n" + "import dagger.Component;\n" + "\n" + "@Component\n" + "class TestComponent {\n" + " @Component.Builder\n" + " class Builder {\n" + " }\n" + "}\n"); } @Test public void defaultPackage() throws Exception { String source = JavaFile.builder("", TypeSpec.classBuilder("HelloWorld") .addMethod(MethodSpec.methodBuilder("main") .addModifiers(Modifier.PUBLIC, Modifier.STATIC) .addParameter(String[].class, "args") .addCode("$T.out.println($S);\n", System.class, "Hello World!") .build()) .build()) .build() .toString(); assertThat(source).isEqualTo("" + "import java.lang.String;\n" + "import java.lang.System;\n" + "\n" + "class HelloWorld {\n" + " public static void main(String[] args) {\n" + " System.out.println(\"Hello World!\");\n" + " }\n" + "}\n"); } @Test public void defaultPackageTypesAreNotImported() throws Exception { String source = JavaFile.builder("hello", TypeSpec.classBuilder("World").addSuperinterface(ClassName.get("", "Test")).build()) .build() .toString(); assertThat(source).isEqualTo("" + "package hello;\n" + "\n" + "class World implements Test {\n" + "}\n"); } @Test public void topOfFileComment() throws Exception { String source = JavaFile.builder("com.squareup.tacos", TypeSpec.classBuilder("Taco").build()) .addFileComment("Generated $L by JavaPoet. DO NOT EDIT!", "2015-01-13") .build() .toString(); assertThat(source).isEqualTo("" + "// Generated 2015-01-13 by JavaPoet. DO NOT EDIT!\n" + "package com.squareup.tacos;\n" + "\n" + "class Taco {\n" + "}\n"); } @Test public void emptyLinesInTopOfFileComment() throws Exception { String source = JavaFile.builder("com.squareup.tacos", TypeSpec.classBuilder("Taco").build()) .addFileComment("\nGENERATED FILE:\n\nDO NOT EDIT!\n") .build() .toString(); assertThat(source).isEqualTo("" + "//\n" + "// GENERATED FILE:\n" + "//\n" + "// DO NOT EDIT!\n" + "//\n" + "package com.squareup.tacos;\n" + "\n" + "class Taco {\n" + "}\n"); } @Test public void packageClassConflictsWithNestedClass() throws Exception { String source = JavaFile.builder("com.squareup.tacos", TypeSpec.classBuilder("Taco") .addField(ClassName.get("com.squareup.tacos", "A"), "a") .addType(TypeSpec.classBuilder("A").build()) .build()) .build() .toString(); assertThat(source).isEqualTo("" + "package com.squareup.tacos;\n" + "\n" + "class Taco {\n" + " com.squareup.tacos.A a;\n" + "\n" + " class A {\n" + " }\n" + "}\n"); } @Test public void packageClassConflictsWithSuperlass() throws Exception { String source = JavaFile.builder("com.squareup.tacos", TypeSpec.classBuilder("Taco") .superclass(ClassName.get("com.taco.bell", "A")) .addField(ClassName.get("com.squareup.tacos", "A"), "a") .build()) .build() .toString(); assertThat(source).isEqualTo("" + "package com.squareup.tacos;\n" + "\n" + "class Taco extends com.taco.bell.A {\n" + " A a;\n" + "}\n"); } @Test public void modifyStaticImports() throws Exception { JavaFile.Builder builder = JavaFile.builder("com.squareup.tacos", TypeSpec.classBuilder("Taco") .build()) .addStaticImport(File.class, "separator"); builder.staticImports.clear(); builder.staticImports.add(File.class.getCanonicalName() + ".separatorChar"); String source = builder.build().toString(); assertThat(source).isEqualTo("" + "package com.squareup.tacos;\n" + "\n" + "import static java.io.File.separatorChar;\n" + "\n" + "class Taco {\n" + "}\n"); } @Test public void alwaysQualifySimple() { String source = JavaFile.builder("com.squareup.tacos", TypeSpec.classBuilder("Taco") .addField(Thread.class, "thread") .alwaysQualify("Thread") .build()) .build() .toString(); assertThat(source).isEqualTo("" + "package com.squareup.tacos;\n" + "\n" + "class Taco {\n" + " java.lang.Thread thread;\n" + "}\n"); } @Test public void alwaysQualifySupersedesJavaLangImports() { String source = JavaFile.builder("com.squareup.tacos", TypeSpec.classBuilder("Taco") .addField(Thread.class, "thread") .alwaysQualify("Thread") .build()) .skipJavaLangImports(true) .build() .toString(); assertThat(source).isEqualTo("" + "package com.squareup.tacos;\n" + "\n" + "class Taco {\n" + " java.lang.Thread thread;\n" + "}\n"); } @Test public void avoidClashesWithNestedClasses_viaClass() { String source = JavaFile.builder("com.squareup.tacos", TypeSpec.classBuilder("Taco") // These two should get qualified .addField(ClassName.get("other", "NestedTypeA"), "nestedA") .addField(ClassName.get("other", "NestedTypeB"), "nestedB") // This one shouldn't since it's not a nested type of Foo .addField(ClassName.get("other", "NestedTypeC"), "nestedC") // This one shouldn't since we only look at nested types .addField(ClassName.get("other", "Foo"), "foo") .avoidClashesWithNestedClasses(Foo.class) .build()) .build() .toString(); assertThat(source).isEqualTo("" + "package com.squareup.tacos;\n" + "\n" + "import other.Foo;\n" + "import other.NestedTypeC;\n" + "\n" + "class Taco {\n" + " other.NestedTypeA nestedA;\n" + "\n" + " other.NestedTypeB nestedB;\n" + "\n" + " NestedTypeC nestedC;\n" + "\n" + " Foo foo;\n" + "}\n"); } @Test public void avoidClashesWithNestedClasses_viaTypeElement() { String source = JavaFile.builder("com.squareup.tacos", TypeSpec.classBuilder("Taco") // These two should get qualified .addField(ClassName.get("other", "NestedTypeA"), "nestedA") .addField(ClassName.get("other", "NestedTypeB"), "nestedB") // This one shouldn't since it's not a nested type of Foo .addField(ClassName.get("other", "NestedTypeC"), "nestedC") // This one shouldn't since we only look at nested types .addField(ClassName.get("other", "Foo"), "foo") .avoidClashesWithNestedClasses(getElement(Foo.class)) .build()) .build() .toString(); assertThat(source).isEqualTo("" + "package com.squareup.tacos;\n" + "\n" + "import other.Foo;\n" + "import other.NestedTypeC;\n" + "\n" + "class Taco {\n" + " other.NestedTypeA nestedA;\n" + "\n" + " other.NestedTypeB nestedB;\n" + "\n" + " NestedTypeC nestedC;\n" + "\n" + " Foo foo;\n" + "}\n"); } @Test public void avoidClashesWithNestedClasses_viaSuperinterfaceType() { String source = JavaFile.builder("com.squareup.tacos", TypeSpec.classBuilder("Taco") // These two should get qualified .addField(ClassName.get("other", "NestedTypeA"), "nestedA") .addField(ClassName.get("other", "NestedTypeB"), "nestedB") // This one shouldn't since it's not a nested type of Foo .addField(ClassName.get("other", "NestedTypeC"), "nestedC") // This one shouldn't since we only look at nested types .addField(ClassName.get("other", "Foo"), "foo") .addType(TypeSpec.classBuilder("NestedTypeA").build()) .addType(TypeSpec.classBuilder("NestedTypeB").build()) .addSuperinterface(FooInterface.class) .build()) .build() .toString(); assertThat(source).isEqualTo("package com.squareup.tacos;\n" + "\n" + "import com.squareup.javapoet.JavaFileTest;\n" + "import other.Foo;\n" + "import other.NestedTypeC;\n" + "\n" + "class Taco implements JavaFileTest.FooInterface {\n" + " other.NestedTypeA nestedA;\n" + "\n" + " other.NestedTypeB nestedB;\n" + "\n" + " NestedTypeC nestedC;\n" + "\n" + " Foo foo;\n" + "\n" + " class NestedTypeA {\n" + " }\n" + "\n" + " class NestedTypeB {\n" + " }\n" + "}\n"); } static class Foo { static class NestedTypeA { } static class NestedTypeB { } } interface FooInterface { class NestedTypeA { } class NestedTypeB { } } private TypeSpec.Builder childTypeBuilder() { return TypeSpec.classBuilder("Child") .addMethod(MethodSpec.methodBuilder("optionalString") .returns(ParameterizedTypeName.get(Optional.class, String.class)) .addStatement("return $T.empty()", Optional.class) .build()) .addMethod(MethodSpec.methodBuilder("pattern") .returns(Pattern.class) .addStatement("return null") .build()); } @Test public void avoidClashes_parentChild_superclass_type() { String source = JavaFile.builder("com.squareup.javapoet", childTypeBuilder().superclass(Parent.class).build()) .build() .toString(); assertThat(source).isEqualTo("package com.squareup.javapoet;\n" + "\n" + "import java.lang.String;\n" + "\n" + "class Child extends JavaFileTest.Parent {\n" + " java.util.Optional<String> optionalString() {\n" + " return java.util.Optional.empty();\n" + " }\n" + "\n" + " java.util.regex.Pattern pattern() {\n" + " return null;\n" + " }\n" + "}\n"); } @Test public void avoidClashes_parentChild_superclass_typeMirror() { String source = JavaFile.builder("com.squareup.javapoet", childTypeBuilder().superclass(getElement(Parent.class).asType()).build()) .build() .toString(); assertThat(source).isEqualTo("package com.squareup.javapoet;\n" + "\n" + "import java.lang.String;\n" + "\n" + "class Child extends JavaFileTest.Parent {\n" + " java.util.Optional<String> optionalString() {\n" + " return java.util.Optional.empty();\n" + " }\n" + "\n" + " java.util.regex.Pattern pattern() {\n" + " return null;\n" + " }\n" + "}\n"); } @Test public void avoidClashes_parentChild_superinterface_type() { String source = JavaFile.builder("com.squareup.javapoet", childTypeBuilder().addSuperinterface(ParentInterface.class).build()) .build() .toString(); assertThat(source).isEqualTo("package com.squareup.javapoet;\n" + "\n" + "import java.lang.String;\n" + "import java.util.regex.Pattern;\n" + "\n" + "class Child implements JavaFileTest.ParentInterface {\n" + " java.util.Optional<String> optionalString() {\n" + " return java.util.Optional.empty();\n" + " }\n" + "\n" + " Pattern pattern() {\n" + " return null;\n" + " }\n" + "}\n"); } @Test public void avoidClashes_parentChild_superinterface_typeMirror() { String source = JavaFile.builder("com.squareup.javapoet", childTypeBuilder().addSuperinterface(getElement(ParentInterface.class).asType()).build()) .build() .toString(); assertThat(source).isEqualTo("package com.squareup.javapoet;\n" + "\n" + "import java.lang.String;\n" + "import java.util.regex.Pattern;\n" + "\n" + "class Child implements JavaFileTest.ParentInterface {\n" + " java.util.Optional<String> optionalString() {\n" + " return java.util.Optional.empty();\n" + " }\n" + "\n" + " Pattern pattern() {\n" + " return null;\n" + " }\n" + "}\n"); } // Regression test for https://github.com/square/javapoet/issues/77 // This covers class and inheritance static class Parent implements ParentInterface { static class Pattern { } } interface ParentInterface { class Optional { } } // Regression test for case raised here: https://github.com/square/javapoet/issues/77#issuecomment-519972404 @Test public void avoidClashes_mapEntry() { String source = JavaFile.builder("com.squareup.javapoet", TypeSpec.classBuilder("MapType") .addMethod(MethodSpec.methodBuilder("optionalString") .returns(ClassName.get("com.foo", "Entry")) .addStatement("return null") .build()) .addSuperinterface(Map.class) .build()) .build() .toString(); assertThat(source).isEqualTo("package com.squareup.javapoet;\n" + "\n" + "import java.util.Map;\n" + "\n" + "class MapType implements Map {\n" + " com.foo.Entry optionalString() {\n" + " return null;\n" + " }\n" + "}\n"); } }