| /* |
| * 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 com.google.common.collect.ImmutableMap; |
| import com.google.testing.compile.CompilationRule; |
| import java.io.IOException; |
| import java.io.Serializable; |
| import java.math.BigDecimal; |
| import java.util.AbstractSet; |
| import java.util.ArrayList; |
| import java.util.Arrays; |
| import java.util.Collections; |
| import java.util.Comparator; |
| import java.util.EventListener; |
| import java.util.List; |
| import java.util.Locale; |
| import java.util.Map; |
| import java.util.Random; |
| import java.util.concurrent.Callable; |
| import javax.lang.model.element.Element; |
| import javax.lang.model.element.Modifier; |
| import javax.lang.model.element.TypeElement; |
| import javax.lang.model.type.TypeMirror; |
| import org.junit.Rule; |
| import org.junit.Test; |
| import org.junit.runner.RunWith; |
| import org.junit.runners.JUnit4; |
| import org.mockito.Mockito; |
| |
| import static com.google.common.truth.Truth.assertThat; |
| import static org.junit.Assert.assertEquals; |
| import static org.junit.Assert.fail; |
| |
| @RunWith(JUnit4.class) |
| public final class TypeSpecTest { |
| private final String tacosPackage = "com.squareup.tacos"; |
| private static final String donutsPackage = "com.squareup.donuts"; |
| |
| @Rule public final CompilationRule compilation = new CompilationRule(); |
| |
| private TypeElement getElement(Class<?> clazz) { |
| return compilation.getElements().getTypeElement(clazz.getCanonicalName()); |
| } |
| |
| @Test public void basic() throws Exception { |
| TypeSpec taco = TypeSpec.classBuilder("Taco") |
| .addMethod(MethodSpec.methodBuilder("toString") |
| .addAnnotation(Override.class) |
| .addModifiers(Modifier.PUBLIC, Modifier.FINAL) |
| .returns(String.class) |
| .addCode("return $S;\n", "taco") |
| .build()) |
| .build(); |
| assertThat(toString(taco)).isEqualTo("" |
| + "package com.squareup.tacos;\n" |
| + "\n" |
| + "import java.lang.Override;\n" |
| + "import java.lang.String;\n" |
| + "\n" |
| + "class Taco {\n" |
| + " @Override\n" |
| + " public final String toString() {\n" |
| + " return \"taco\";\n" |
| + " }\n" |
| + "}\n"); |
| assertEquals(472949424, taco.hashCode()); // update expected number if source changes |
| } |
| |
| @Test public void interestingTypes() throws Exception { |
| TypeName listOfAny = ParameterizedTypeName.get( |
| ClassName.get(List.class), WildcardTypeName.subtypeOf(Object.class)); |
| TypeName listOfExtends = ParameterizedTypeName.get( |
| ClassName.get(List.class), WildcardTypeName.subtypeOf(Serializable.class)); |
| TypeName listOfSuper = ParameterizedTypeName.get(ClassName.get(List.class), |
| WildcardTypeName.supertypeOf(String.class)); |
| TypeSpec taco = TypeSpec.classBuilder("Taco") |
| .addField(listOfAny, "extendsObject") |
| .addField(listOfExtends, "extendsSerializable") |
| .addField(listOfSuper, "superString") |
| .build(); |
| assertThat(toString(taco)).isEqualTo("" |
| + "package com.squareup.tacos;\n" |
| + "\n" |
| + "import java.io.Serializable;\n" |
| + "import java.lang.String;\n" |
| + "import java.util.List;\n" |
| + "\n" |
| + "class Taco {\n" |
| + " List<?> extendsObject;\n" |
| + "\n" |
| + " List<? extends Serializable> extendsSerializable;\n" |
| + "\n" |
| + " List<? super String> superString;\n" |
| + "}\n"); |
| } |
| |
| @Test public void anonymousInnerClass() throws Exception { |
| ClassName foo = ClassName.get(tacosPackage, "Foo"); |
| ClassName bar = ClassName.get(tacosPackage, "Bar"); |
| ClassName thingThang = ClassName.get(tacosPackage, "Thing", "Thang"); |
| TypeName thingThangOfFooBar = ParameterizedTypeName.get(thingThang, foo, bar); |
| ClassName thung = ClassName.get(tacosPackage, "Thung"); |
| ClassName simpleThung = ClassName.get(tacosPackage, "SimpleThung"); |
| TypeName thungOfSuperBar = ParameterizedTypeName.get(thung, WildcardTypeName.supertypeOf(bar)); |
| TypeName thungOfSuperFoo = ParameterizedTypeName.get(thung, WildcardTypeName.supertypeOf(foo)); |
| TypeName simpleThungOfBar = ParameterizedTypeName.get(simpleThung, bar); |
| |
| ParameterSpec thungParameter = ParameterSpec.builder(thungOfSuperFoo, "thung") |
| .addModifiers(Modifier.FINAL) |
| .build(); |
| TypeSpec aSimpleThung = TypeSpec.anonymousClassBuilder(CodeBlock.of("$N", thungParameter)) |
| .superclass(simpleThungOfBar) |
| .addMethod(MethodSpec.methodBuilder("doSomething") |
| .addAnnotation(Override.class) |
| .addModifiers(Modifier.PUBLIC) |
| .addParameter(bar, "bar") |
| .addCode("/* code snippets */\n") |
| .build()) |
| .build(); |
| TypeSpec aThingThang = TypeSpec.anonymousClassBuilder("") |
| .superclass(thingThangOfFooBar) |
| .addMethod(MethodSpec.methodBuilder("call") |
| .addAnnotation(Override.class) |
| .addModifiers(Modifier.PUBLIC) |
| .returns(thungOfSuperBar) |
| .addParameter(thungParameter) |
| .addCode("return $L;\n", aSimpleThung) |
| .build()) |
| .build(); |
| TypeSpec taco = TypeSpec.classBuilder("Taco") |
| .addField(FieldSpec.builder(thingThangOfFooBar, "NAME") |
| .addModifiers(Modifier.STATIC, Modifier.FINAL, Modifier.FINAL) |
| .initializer("$L", aThingThang) |
| .build()) |
| .build(); |
| |
| assertThat(toString(taco)).isEqualTo("" |
| + "package com.squareup.tacos;\n" |
| + "\n" |
| + "import java.lang.Override;\n" |
| + "\n" |
| + "class Taco {\n" |
| + " static final Thing.Thang<Foo, Bar> NAME = new Thing.Thang<Foo, Bar>() {\n" |
| + " @Override\n" |
| + " public Thung<? super Bar> call(final Thung<? super Foo> thung) {\n" |
| + " return new SimpleThung<Bar>(thung) {\n" |
| + " @Override\n" |
| + " public void doSomething(Bar bar) {\n" |
| + " /* code snippets */\n" |
| + " }\n" |
| + " };\n" |
| + " }\n" |
| + " };\n" |
| + "}\n"); |
| } |
| |
| @Test public void annotatedParameters() throws Exception { |
| TypeSpec service = TypeSpec.classBuilder("Foo") |
| .addMethod(MethodSpec.constructorBuilder() |
| .addModifiers(Modifier.PUBLIC) |
| .addParameter(long.class, "id") |
| .addParameter(ParameterSpec.builder(String.class, "one") |
| .addAnnotation(ClassName.get(tacosPackage, "Ping")) |
| .build()) |
| .addParameter(ParameterSpec.builder(String.class, "two") |
| .addAnnotation(ClassName.get(tacosPackage, "Ping")) |
| .build()) |
| .addParameter(ParameterSpec.builder(String.class, "three") |
| .addAnnotation(AnnotationSpec.builder(ClassName.get(tacosPackage, "Pong")) |
| .addMember("value", "$S", "pong") |
| .build()) |
| .build()) |
| .addParameter(ParameterSpec.builder(String.class, "four") |
| .addAnnotation(ClassName.get(tacosPackage, "Ping")) |
| .build()) |
| .addCode("/* code snippets */\n") |
| .build()) |
| .build(); |
| |
| assertThat(toString(service)).isEqualTo("" |
| + "package com.squareup.tacos;\n" |
| + "\n" |
| + "import java.lang.String;\n" |
| + "\n" |
| + "class Foo {\n" |
| + " public Foo(long id, @Ping String one, @Ping String two, @Pong(\"pong\") String three,\n" |
| + " @Ping String four) {\n" |
| + " /* code snippets */\n" |
| + " }\n" |
| + "}\n"); |
| } |
| |
| /** |
| * We had a bug where annotations were preventing us from doing the right thing when resolving |
| * imports. https://github.com/square/javapoet/issues/422 |
| */ |
| @Test public void annotationsAndJavaLangTypes() throws Exception { |
| ClassName freeRange = ClassName.get("javax.annotation", "FreeRange"); |
| TypeSpec taco = TypeSpec.classBuilder("EthicalTaco") |
| .addField(ClassName.get(String.class) |
| .annotated(AnnotationSpec.builder(freeRange).build()), "meat") |
| .build(); |
| |
| assertThat(toString(taco)).isEqualTo("" |
| + "package com.squareup.tacos;\n" |
| + "\n" |
| + "import java.lang.String;\n" |
| + "import javax.annotation.FreeRange;\n" |
| + "\n" |
| + "class EthicalTaco {\n" |
| + " @FreeRange String meat;\n" |
| + "}\n"); |
| } |
| |
| @Test public void retrofitStyleInterface() throws Exception { |
| ClassName observable = ClassName.get(tacosPackage, "Observable"); |
| ClassName fooBar = ClassName.get(tacosPackage, "FooBar"); |
| ClassName thing = ClassName.get(tacosPackage, "Thing"); |
| ClassName things = ClassName.get(tacosPackage, "Things"); |
| ClassName map = ClassName.get("java.util", "Map"); |
| ClassName string = ClassName.get("java.lang", "String"); |
| ClassName headers = ClassName.get(tacosPackage, "Headers"); |
| ClassName post = ClassName.get(tacosPackage, "POST"); |
| ClassName body = ClassName.get(tacosPackage, "Body"); |
| ClassName queryMap = ClassName.get(tacosPackage, "QueryMap"); |
| ClassName header = ClassName.get(tacosPackage, "Header"); |
| TypeSpec service = TypeSpec.interfaceBuilder("Service") |
| .addMethod(MethodSpec.methodBuilder("fooBar") |
| .addModifiers(Modifier.PUBLIC, Modifier.ABSTRACT) |
| .addAnnotation(AnnotationSpec.builder(headers) |
| .addMember("value", "$S", "Accept: application/json") |
| .addMember("value", "$S", "User-Agent: foobar") |
| .build()) |
| .addAnnotation(AnnotationSpec.builder(post) |
| .addMember("value", "$S", "/foo/bar") |
| .build()) |
| .returns(ParameterizedTypeName.get(observable, fooBar)) |
| .addParameter(ParameterSpec.builder(ParameterizedTypeName.get(things, thing), "things") |
| .addAnnotation(body) |
| .build()) |
| .addParameter(ParameterSpec.builder( |
| ParameterizedTypeName.get(map, string, string), "query") |
| .addAnnotation(AnnotationSpec.builder(queryMap) |
| .addMember("encodeValues", "false") |
| .build()) |
| .build()) |
| .addParameter(ParameterSpec.builder(string, "authorization") |
| .addAnnotation(AnnotationSpec.builder(header) |
| .addMember("value", "$S", "Authorization") |
| .build()) |
| .build()) |
| .build()) |
| .build(); |
| |
| assertThat(toString(service)).isEqualTo("" |
| + "package com.squareup.tacos;\n" |
| + "\n" |
| + "import java.lang.String;\n" |
| + "import java.util.Map;\n" |
| + "\n" |
| + "interface Service {\n" |
| + " @Headers({\n" |
| + " \"Accept: application/json\",\n" |
| + " \"User-Agent: foobar\"\n" |
| + " })\n" |
| + " @POST(\"/foo/bar\")\n" |
| + " Observable<FooBar> fooBar(@Body Things<Thing> things,\n" |
| + " @QueryMap(encodeValues = false) Map<String, String> query,\n" |
| + " @Header(\"Authorization\") String authorization);\n" |
| + "}\n"); |
| } |
| |
| @Test public void annotatedField() throws Exception { |
| TypeSpec taco = TypeSpec.classBuilder("Taco") |
| .addField(FieldSpec.builder(String.class, "thing", Modifier.PRIVATE, Modifier.FINAL) |
| .addAnnotation(AnnotationSpec.builder(ClassName.get(tacosPackage, "JsonAdapter")) |
| .addMember("value", "$T.class", ClassName.get(tacosPackage, "Foo")) |
| .build()) |
| .build()) |
| .build(); |
| assertThat(toString(taco)).isEqualTo("" |
| + "package com.squareup.tacos;\n" |
| + "\n" |
| + "import java.lang.String;\n" |
| + "\n" |
| + "class Taco {\n" |
| + " @JsonAdapter(Foo.class)\n" |
| + " private final String thing;\n" |
| + "}\n"); |
| } |
| |
| @Test public void annotatedClass() throws Exception { |
| ClassName someType = ClassName.get(tacosPackage, "SomeType"); |
| TypeSpec taco = TypeSpec.classBuilder("Foo") |
| .addAnnotation(AnnotationSpec.builder(ClassName.get(tacosPackage, "Something")) |
| .addMember("hi", "$T.$N", someType, "FIELD") |
| .addMember("hey", "$L", 12) |
| .addMember("hello", "$S", "goodbye") |
| .build()) |
| .addModifiers(Modifier.PUBLIC) |
| .build(); |
| assertThat(toString(taco)).isEqualTo("" |
| + "package com.squareup.tacos;\n" |
| + "\n" |
| + "@Something(\n" |
| + " hi = SomeType.FIELD,\n" |
| + " hey = 12,\n" |
| + " hello = \"goodbye\"\n" |
| + ")\n" |
| + "public class Foo {\n" |
| + "}\n"); |
| } |
| |
| @Test public void addAnnotationDisallowsNull() { |
| try { |
| TypeSpec.classBuilder("Foo").addAnnotation((AnnotationSpec) null); |
| fail(); |
| } catch (NullPointerException expected) { |
| assertThat(expected).hasMessageThat().isEqualTo("annotationSpec == null"); |
| } |
| try { |
| TypeSpec.classBuilder("Foo").addAnnotation((ClassName) null); |
| fail(); |
| } catch (NullPointerException expected) { |
| assertThat(expected).hasMessageThat().isEqualTo("type == null"); |
| } |
| try { |
| TypeSpec.classBuilder("Foo").addAnnotation((Class<?>) null); |
| fail(); |
| } catch (NullPointerException expected) { |
| assertThat(expected).hasMessageThat().isEqualTo("clazz == null"); |
| } |
| } |
| |
| @Test public void enumWithSubclassing() throws Exception { |
| TypeSpec roshambo = TypeSpec.enumBuilder("Roshambo") |
| .addModifiers(Modifier.PUBLIC) |
| .addEnumConstant("ROCK", TypeSpec.anonymousClassBuilder("") |
| .addJavadoc("Avalanche!\n") |
| .build()) |
| .addEnumConstant("PAPER", TypeSpec.anonymousClassBuilder("$S", "flat") |
| .addMethod(MethodSpec.methodBuilder("toString") |
| .addAnnotation(Override.class) |
| .addModifiers(Modifier.PUBLIC) |
| .returns(String.class) |
| .addCode("return $S;\n", "paper airplane!") |
| .build()) |
| .build()) |
| .addEnumConstant("SCISSORS", TypeSpec.anonymousClassBuilder("$S", "peace sign") |
| .build()) |
| .addField(String.class, "handPosition", Modifier.PRIVATE, Modifier.FINAL) |
| .addMethod(MethodSpec.constructorBuilder() |
| .addParameter(String.class, "handPosition") |
| .addCode("this.handPosition = handPosition;\n") |
| .build()) |
| .addMethod(MethodSpec.constructorBuilder() |
| .addCode("this($S);\n", "fist") |
| .build()) |
| .build(); |
| assertThat(toString(roshambo)).isEqualTo("" |
| + "package com.squareup.tacos;\n" |
| + "\n" |
| + "import java.lang.Override;\n" |
| + "import java.lang.String;\n" |
| + "\n" |
| + "public enum Roshambo {\n" |
| + " /**\n" |
| + " * Avalanche!\n" |
| + " */\n" |
| + " ROCK,\n" |
| + "\n" |
| + " PAPER(\"flat\") {\n" |
| + " @Override\n" |
| + " public String toString() {\n" |
| + " return \"paper airplane!\";\n" |
| + " }\n" |
| + " },\n" |
| + "\n" |
| + " SCISSORS(\"peace sign\");\n" |
| + "\n" |
| + " private final String handPosition;\n" |
| + "\n" |
| + " Roshambo(String handPosition) {\n" |
| + " this.handPosition = handPosition;\n" |
| + " }\n" |
| + "\n" |
| + " Roshambo() {\n" |
| + " this(\"fist\");\n" |
| + " }\n" |
| + "}\n"); |
| } |
| |
| /** https://github.com/square/javapoet/issues/193 */ |
| @Test public void enumsMayDefineAbstractMethods() throws Exception { |
| TypeSpec roshambo = TypeSpec.enumBuilder("Tortilla") |
| .addModifiers(Modifier.PUBLIC) |
| .addEnumConstant("CORN", TypeSpec.anonymousClassBuilder("") |
| .addMethod(MethodSpec.methodBuilder("fold") |
| .addAnnotation(Override.class) |
| .addModifiers(Modifier.PUBLIC) |
| .build()) |
| .build()) |
| .addMethod(MethodSpec.methodBuilder("fold") |
| .addModifiers(Modifier.PUBLIC, Modifier.ABSTRACT) |
| .build()) |
| .build(); |
| assertThat(toString(roshambo)).isEqualTo("" |
| + "package com.squareup.tacos;\n" |
| + "\n" |
| + "import java.lang.Override;\n" |
| + "\n" |
| + "public enum Tortilla {\n" |
| + " CORN {\n" |
| + " @Override\n" |
| + " public void fold() {\n" |
| + " }\n" |
| + " };\n" |
| + "\n" |
| + " public abstract void fold();\n" |
| + "}\n"); |
| } |
| |
| @Test public void enumConstantsRequired() throws Exception { |
| try { |
| TypeSpec.enumBuilder("Roshambo") |
| .build(); |
| fail(); |
| } catch (IllegalArgumentException expected) { |
| } |
| } |
| |
| @Test public void onlyEnumsMayHaveEnumConstants() throws Exception { |
| try { |
| TypeSpec.classBuilder("Roshambo") |
| .addEnumConstant("ROCK") |
| .build(); |
| fail(); |
| } catch (IllegalStateException expected) { |
| } |
| } |
| |
| @Test public void enumWithMembersButNoConstructorCall() throws Exception { |
| TypeSpec roshambo = TypeSpec.enumBuilder("Roshambo") |
| .addEnumConstant("SPOCK", TypeSpec.anonymousClassBuilder("") |
| .addMethod(MethodSpec.methodBuilder("toString") |
| .addAnnotation(Override.class) |
| .addModifiers(Modifier.PUBLIC) |
| .returns(String.class) |
| .addCode("return $S;\n", "west side") |
| .build()) |
| .build()) |
| .build(); |
| assertThat(toString(roshambo)).isEqualTo("" |
| + "package com.squareup.tacos;\n" |
| + "\n" |
| + "import java.lang.Override;\n" |
| + "import java.lang.String;\n" |
| + "\n" |
| + "enum Roshambo {\n" |
| + " SPOCK {\n" |
| + " @Override\n" |
| + " public String toString() {\n" |
| + " return \"west side\";\n" |
| + " }\n" |
| + " }\n" |
| + "}\n"); |
| } |
| |
| /** https://github.com/square/javapoet/issues/253 */ |
| @Test public void enumWithAnnotatedValues() throws Exception { |
| TypeSpec roshambo = TypeSpec.enumBuilder("Roshambo") |
| .addModifiers(Modifier.PUBLIC) |
| .addEnumConstant("ROCK", TypeSpec.anonymousClassBuilder("") |
| .addAnnotation(Deprecated.class) |
| .build()) |
| .addEnumConstant("PAPER") |
| .addEnumConstant("SCISSORS") |
| .build(); |
| assertThat(toString(roshambo)).isEqualTo("" |
| + "package com.squareup.tacos;\n" |
| + "\n" |
| + "import java.lang.Deprecated;\n" |
| + "\n" |
| + "public enum Roshambo {\n" |
| + " @Deprecated\n" |
| + " ROCK,\n" |
| + "\n" |
| + " PAPER,\n" |
| + "\n" |
| + " SCISSORS\n" |
| + "}\n"); |
| } |
| |
| @Test public void methodThrows() throws Exception { |
| TypeSpec taco = TypeSpec.classBuilder("Taco") |
| .addModifiers(Modifier.ABSTRACT) |
| .addMethod(MethodSpec.methodBuilder("throwOne") |
| .addException(IOException.class) |
| .build()) |
| .addMethod(MethodSpec.methodBuilder("throwTwo") |
| .addException(IOException.class) |
| .addException(ClassName.get(tacosPackage, "SourCreamException")) |
| .build()) |
| .addMethod(MethodSpec.methodBuilder("abstractThrow") |
| .addModifiers(Modifier.ABSTRACT) |
| .addException(IOException.class) |
| .build()) |
| .addMethod(MethodSpec.methodBuilder("nativeThrow") |
| .addModifiers(Modifier.NATIVE) |
| .addException(IOException.class) |
| .build()) |
| .build(); |
| assertThat(toString(taco)).isEqualTo("" |
| + "package com.squareup.tacos;\n" |
| + "\n" |
| + "import java.io.IOException;\n" |
| + "\n" |
| + "abstract class Taco {\n" |
| + " void throwOne() throws IOException {\n" |
| + " }\n" |
| + "\n" |
| + " void throwTwo() throws IOException, SourCreamException {\n" |
| + " }\n" |
| + "\n" |
| + " abstract void abstractThrow() throws IOException;\n" |
| + "\n" |
| + " native void nativeThrow() throws IOException;\n" |
| + "}\n"); |
| } |
| |
| @Test public void typeVariables() throws Exception { |
| TypeVariableName t = TypeVariableName.get("T"); |
| TypeVariableName p = TypeVariableName.get("P", Number.class); |
| ClassName location = ClassName.get(tacosPackage, "Location"); |
| TypeSpec typeSpec = TypeSpec.classBuilder("Location") |
| .addTypeVariable(t) |
| .addTypeVariable(p) |
| .addSuperinterface(ParameterizedTypeName.get(ClassName.get(Comparable.class), p)) |
| .addField(t, "label") |
| .addField(p, "x") |
| .addField(p, "y") |
| .addMethod(MethodSpec.methodBuilder("compareTo") |
| .addAnnotation(Override.class) |
| .addModifiers(Modifier.PUBLIC) |
| .returns(int.class) |
| .addParameter(p, "p") |
| .addCode("return 0;\n") |
| .build()) |
| .addMethod(MethodSpec.methodBuilder("of") |
| .addModifiers(Modifier.PUBLIC, Modifier.STATIC) |
| .addTypeVariable(t) |
| .addTypeVariable(p) |
| .returns(ParameterizedTypeName.get(location, t, p)) |
| .addParameter(t, "label") |
| .addParameter(p, "x") |
| .addParameter(p, "y") |
| .addCode("throw new $T($S);\n", UnsupportedOperationException.class, "TODO") |
| .build()) |
| .build(); |
| assertThat(toString(typeSpec)).isEqualTo("" |
| + "package com.squareup.tacos;\n" |
| + "\n" |
| + "import java.lang.Comparable;\n" |
| + "import java.lang.Number;\n" |
| + "import java.lang.Override;\n" |
| + "import java.lang.UnsupportedOperationException;\n" |
| + "\n" |
| + "class Location<T, P extends Number> implements Comparable<P> {\n" |
| + " T label;\n" |
| + "\n" |
| + " P x;\n" |
| + "\n" |
| + " P y;\n" |
| + "\n" |
| + " @Override\n" |
| + " public int compareTo(P p) {\n" |
| + " return 0;\n" |
| + " }\n" |
| + "\n" |
| + " public static <T, P extends Number> Location<T, P> of(T label, P x, P y) {\n" |
| + " throw new UnsupportedOperationException(\"TODO\");\n" |
| + " }\n" |
| + "}\n"); |
| } |
| |
| @Test public void typeVariableWithBounds() { |
| AnnotationSpec a = AnnotationSpec.builder(ClassName.get("com.squareup.tacos", "A")).build(); |
| TypeVariableName p = TypeVariableName.get("P", Number.class); |
| TypeVariableName q = (TypeVariableName) TypeVariableName.get("Q", Number.class).annotated(a); |
| TypeSpec typeSpec = TypeSpec.classBuilder("Location") |
| .addTypeVariable(p.withBounds(Comparable.class)) |
| .addTypeVariable(q.withBounds(Comparable.class)) |
| .addField(p, "x") |
| .addField(q, "y") |
| .build(); |
| assertThat(toString(typeSpec)).isEqualTo("" |
| + "package com.squareup.tacos;\n" |
| + "\n" |
| + "import java.lang.Comparable;\n" |
| + "import java.lang.Number;\n" |
| + "\n" |
| + "class Location<P extends Number & Comparable, @A Q extends Number & Comparable> {\n" |
| + " P x;\n" |
| + "\n" |
| + " @A Q y;\n" |
| + "}\n"); |
| } |
| |
| @Test public void classImplementsExtends() throws Exception { |
| ClassName taco = ClassName.get(tacosPackage, "Taco"); |
| ClassName food = ClassName.get("com.squareup.tacos", "Food"); |
| TypeSpec typeSpec = TypeSpec.classBuilder("Taco") |
| .addModifiers(Modifier.ABSTRACT) |
| .superclass(ParameterizedTypeName.get(ClassName.get(AbstractSet.class), food)) |
| .addSuperinterface(Serializable.class) |
| .addSuperinterface(ParameterizedTypeName.get(ClassName.get(Comparable.class), taco)) |
| .build(); |
| assertThat(toString(typeSpec)).isEqualTo("" |
| + "package com.squareup.tacos;\n" |
| + "\n" |
| + "import java.io.Serializable;\n" |
| + "import java.lang.Comparable;\n" |
| + "import java.util.AbstractSet;\n" |
| + "\n" |
| + "abstract class Taco extends AbstractSet<Food> " |
| + "implements Serializable, Comparable<Taco> {\n" |
| + "}\n"); |
| } |
| |
| @Test public void classImplementsNestedClass() throws Exception { |
| ClassName outer = ClassName.get(tacosPackage, "Outer"); |
| ClassName inner = outer.nestedClass("Inner"); |
| ClassName callable = ClassName.get(Callable.class); |
| TypeSpec typeSpec = TypeSpec.classBuilder("Outer") |
| .superclass(ParameterizedTypeName.get(callable, |
| inner)) |
| .addType(TypeSpec.classBuilder("Inner") |
| .addModifiers(Modifier.STATIC) |
| .build()) |
| .build(); |
| |
| assertThat(toString(typeSpec)).isEqualTo("" |
| + "package com.squareup.tacos;\n" |
| + "\n" |
| + "import java.util.concurrent.Callable;\n" |
| + "\n" |
| + "class Outer extends Callable<Outer.Inner> {\n" |
| + " static class Inner {\n" |
| + " }\n" |
| + "}\n"); |
| } |
| |
| @Test public void enumImplements() throws Exception { |
| TypeSpec typeSpec = TypeSpec.enumBuilder("Food") |
| .addSuperinterface(Serializable.class) |
| .addSuperinterface(Cloneable.class) |
| .addEnumConstant("LEAN_GROUND_BEEF") |
| .addEnumConstant("SHREDDED_CHEESE") |
| .build(); |
| assertThat(toString(typeSpec)).isEqualTo("" |
| + "package com.squareup.tacos;\n" |
| + "\n" |
| + "import java.io.Serializable;\n" |
| + "import java.lang.Cloneable;\n" |
| + "\n" |
| + "enum Food implements Serializable, Cloneable {\n" |
| + " LEAN_GROUND_BEEF,\n" |
| + "\n" |
| + " SHREDDED_CHEESE\n" |
| + "}\n"); |
| } |
| |
| @Test public void interfaceExtends() throws Exception { |
| ClassName taco = ClassName.get(tacosPackage, "Taco"); |
| TypeSpec typeSpec = TypeSpec.interfaceBuilder("Taco") |
| .addSuperinterface(Serializable.class) |
| .addSuperinterface(ParameterizedTypeName.get(ClassName.get(Comparable.class), taco)) |
| .build(); |
| assertThat(toString(typeSpec)).isEqualTo("" |
| + "package com.squareup.tacos;\n" |
| + "\n" |
| + "import java.io.Serializable;\n" |
| + "import java.lang.Comparable;\n" |
| + "\n" |
| + "interface Taco extends Serializable, Comparable<Taco> {\n" |
| + "}\n"); |
| } |
| |
| @Test public void nestedClasses() throws Exception { |
| ClassName taco = ClassName.get(tacosPackage, "Combo", "Taco"); |
| ClassName topping = ClassName.get(tacosPackage, "Combo", "Taco", "Topping"); |
| ClassName chips = ClassName.get(tacosPackage, "Combo", "Chips"); |
| ClassName sauce = ClassName.get(tacosPackage, "Combo", "Sauce"); |
| TypeSpec typeSpec = TypeSpec.classBuilder("Combo") |
| .addField(taco, "taco") |
| .addField(chips, "chips") |
| .addType(TypeSpec.classBuilder(taco.simpleName()) |
| .addModifiers(Modifier.STATIC) |
| .addField(ParameterizedTypeName.get(ClassName.get(List.class), topping), "toppings") |
| .addField(sauce, "sauce") |
| .addType(TypeSpec.enumBuilder(topping.simpleName()) |
| .addEnumConstant("SHREDDED_CHEESE") |
| .addEnumConstant("LEAN_GROUND_BEEF") |
| .build()) |
| .build()) |
| .addType(TypeSpec.classBuilder(chips.simpleName()) |
| .addModifiers(Modifier.STATIC) |
| .addField(topping, "topping") |
| .addField(sauce, "dippingSauce") |
| .build()) |
| .addType(TypeSpec.enumBuilder(sauce.simpleName()) |
| .addEnumConstant("SOUR_CREAM") |
| .addEnumConstant("SALSA") |
| .addEnumConstant("QUESO") |
| .addEnumConstant("MILD") |
| .addEnumConstant("FIRE") |
| .build()) |
| .build(); |
| |
| assertThat(toString(typeSpec)).isEqualTo("" |
| + "package com.squareup.tacos;\n" |
| + "\n" |
| + "import java.util.List;\n" |
| + "\n" |
| + "class Combo {\n" |
| + " Taco taco;\n" |
| + "\n" |
| + " Chips chips;\n" |
| + "\n" |
| + " static class Taco {\n" |
| + " List<Topping> toppings;\n" |
| + "\n" |
| + " Sauce sauce;\n" |
| + "\n" |
| + " enum Topping {\n" |
| + " SHREDDED_CHEESE,\n" |
| + "\n" |
| + " LEAN_GROUND_BEEF\n" |
| + " }\n" |
| + " }\n" |
| + "\n" |
| + " static class Chips {\n" |
| + " Taco.Topping topping;\n" |
| + "\n" |
| + " Sauce dippingSauce;\n" |
| + " }\n" |
| + "\n" |
| + " enum Sauce {\n" |
| + " SOUR_CREAM,\n" |
| + "\n" |
| + " SALSA,\n" |
| + "\n" |
| + " QUESO,\n" |
| + "\n" |
| + " MILD,\n" |
| + "\n" |
| + " FIRE\n" |
| + " }\n" |
| + "}\n"); |
| } |
| |
| @Test public void annotation() throws Exception { |
| TypeSpec annotation = TypeSpec.annotationBuilder("MyAnnotation") |
| .addModifiers(Modifier.PUBLIC) |
| .addMethod(MethodSpec.methodBuilder("test") |
| .addModifiers(Modifier.PUBLIC, Modifier.ABSTRACT) |
| .defaultValue("$L", 0) |
| .returns(int.class) |
| .build()) |
| .build(); |
| |
| assertThat(toString(annotation)).isEqualTo("" |
| + "package com.squareup.tacos;\n" |
| + "\n" |
| + "public @interface MyAnnotation {\n" |
| + " int test() default 0;\n" |
| + "}\n" |
| ); |
| } |
| |
| @Test public void innerAnnotationInAnnotationDeclaration() throws Exception { |
| TypeSpec bar = TypeSpec.annotationBuilder("Bar") |
| .addMethod(MethodSpec.methodBuilder("value") |
| .addModifiers(Modifier.PUBLIC, Modifier.ABSTRACT) |
| .defaultValue("@$T", Deprecated.class) |
| .returns(Deprecated.class) |
| .build()) |
| .build(); |
| |
| assertThat(toString(bar)).isEqualTo("" |
| + "package com.squareup.tacos;\n" |
| + "\n" |
| + "import java.lang.Deprecated;\n" |
| + "\n" |
| + "@interface Bar {\n" |
| + " Deprecated value() default @Deprecated;\n" |
| + "}\n" |
| ); |
| } |
| |
| @Test public void annotationWithFields() { |
| FieldSpec field = FieldSpec.builder(int.class, "FOO") |
| .addModifiers(Modifier.PUBLIC, Modifier.STATIC, Modifier.FINAL) |
| .initializer("$L", 101) |
| .build(); |
| |
| TypeSpec anno = TypeSpec.annotationBuilder("Anno") |
| .addField(field) |
| .build(); |
| |
| assertThat(toString(anno)).isEqualTo("" |
| + "package com.squareup.tacos;\n" |
| + "\n" |
| + "@interface Anno {\n" |
| + " int FOO = 101;\n" |
| + "}\n" |
| ); |
| } |
| |
| @Test |
| public void classCannotHaveDefaultValueForMethod() throws Exception { |
| try { |
| TypeSpec.classBuilder("Tacos") |
| .addMethod(MethodSpec.methodBuilder("test") |
| .addModifiers(Modifier.PUBLIC) |
| .defaultValue("0") |
| .returns(int.class) |
| .build()) |
| .build(); |
| fail(); |
| } catch (IllegalStateException expected) { |
| } |
| } |
| |
| @Test |
| public void classCannotHaveDefaultMethods() throws Exception { |
| try { |
| TypeSpec.classBuilder("Tacos") |
| .addMethod(MethodSpec.methodBuilder("test") |
| .addModifiers(Modifier.PUBLIC, Modifier.DEFAULT) |
| .returns(int.class) |
| .addCode(CodeBlock.builder().addStatement("return 0").build()) |
| .build()) |
| .build(); |
| fail(); |
| } catch (IllegalStateException expected) { |
| } |
| } |
| |
| @Test |
| public void interfaceStaticMethods() throws Exception { |
| TypeSpec bar = TypeSpec.interfaceBuilder("Tacos") |
| .addMethod(MethodSpec.methodBuilder("test") |
| .addModifiers(Modifier.PUBLIC, Modifier.STATIC) |
| .returns(int.class) |
| .addCode(CodeBlock.builder().addStatement("return 0").build()) |
| .build()) |
| .build(); |
| |
| assertThat(toString(bar)).isEqualTo("" |
| + "package com.squareup.tacos;\n" |
| + "\n" |
| + "interface Tacos {\n" |
| + " static int test() {\n" |
| + " return 0;\n" |
| + " }\n" |
| + "}\n" |
| ); |
| } |
| |
| @Test |
| public void interfaceDefaultMethods() throws Exception { |
| TypeSpec bar = TypeSpec.interfaceBuilder("Tacos") |
| .addMethod(MethodSpec.methodBuilder("test") |
| .addModifiers(Modifier.PUBLIC, Modifier.DEFAULT) |
| .returns(int.class) |
| .addCode(CodeBlock.builder().addStatement("return 0").build()) |
| .build()) |
| .build(); |
| |
| assertThat(toString(bar)).isEqualTo("" |
| + "package com.squareup.tacos;\n" |
| + "\n" |
| + "interface Tacos {\n" |
| + " default int test() {\n" |
| + " return 0;\n" |
| + " }\n" |
| + "}\n" |
| ); |
| } |
| |
| @Test public void referencedAndDeclaredSimpleNamesConflict() throws Exception { |
| FieldSpec internalTop = FieldSpec.builder( |
| ClassName.get(tacosPackage, "Top"), "internalTop").build(); |
| FieldSpec internalBottom = FieldSpec.builder( |
| ClassName.get(tacosPackage, "Top", "Middle", "Bottom"), "internalBottom").build(); |
| FieldSpec externalTop = FieldSpec.builder( |
| ClassName.get(donutsPackage, "Top"), "externalTop").build(); |
| FieldSpec externalBottom = FieldSpec.builder( |
| ClassName.get(donutsPackage, "Bottom"), "externalBottom").build(); |
| TypeSpec top = TypeSpec.classBuilder("Top") |
| .addField(internalTop) |
| .addField(internalBottom) |
| .addField(externalTop) |
| .addField(externalBottom) |
| .addType(TypeSpec.classBuilder("Middle") |
| .addField(internalTop) |
| .addField(internalBottom) |
| .addField(externalTop) |
| .addField(externalBottom) |
| .addType(TypeSpec.classBuilder("Bottom") |
| .addField(internalTop) |
| .addField(internalBottom) |
| .addField(externalTop) |
| .addField(externalBottom) |
| .build()) |
| .build()) |
| .build(); |
| assertThat(toString(top)).isEqualTo("" |
| + "package com.squareup.tacos;\n" |
| + "\n" |
| + "import com.squareup.donuts.Bottom;\n" |
| + "\n" |
| + "class Top {\n" |
| + " Top internalTop;\n" |
| + "\n" |
| + " Middle.Bottom internalBottom;\n" |
| + "\n" |
| + " com.squareup.donuts.Top externalTop;\n" |
| + "\n" |
| + " Bottom externalBottom;\n" |
| + "\n" |
| + " class Middle {\n" |
| + " Top internalTop;\n" |
| + "\n" |
| + " Bottom internalBottom;\n" |
| + "\n" |
| + " com.squareup.donuts.Top externalTop;\n" |
| + "\n" |
| + " com.squareup.donuts.Bottom externalBottom;\n" |
| + "\n" |
| + " class Bottom {\n" |
| + " Top internalTop;\n" |
| + "\n" |
| + " Bottom internalBottom;\n" |
| + "\n" |
| + " com.squareup.donuts.Top externalTop;\n" |
| + "\n" |
| + " com.squareup.donuts.Bottom externalBottom;\n" |
| + " }\n" |
| + " }\n" |
| + "}\n"); |
| } |
| |
| @Test public void simpleNamesConflictInThisAndOtherPackage() throws Exception { |
| FieldSpec internalOther = FieldSpec.builder( |
| ClassName.get(tacosPackage, "Other"), "internalOther").build(); |
| FieldSpec externalOther = FieldSpec.builder( |
| ClassName.get(donutsPackage, "Other"), "externalOther").build(); |
| TypeSpec gen = TypeSpec.classBuilder("Gen") |
| .addField(internalOther) |
| .addField(externalOther) |
| .build(); |
| assertThat(toString(gen)).isEqualTo("" |
| + "package com.squareup.tacos;\n" |
| + "\n" |
| + "class Gen {\n" |
| + " Other internalOther;\n" |
| + "\n" |
| + " com.squareup.donuts.Other externalOther;\n" |
| + "}\n"); |
| } |
| |
| @Test public void originatingElementsIncludesThoseOfNestedTypes() { |
| Element outerElement = Mockito.mock(Element.class); |
| Element innerElement = Mockito.mock(Element.class); |
| TypeSpec outer = TypeSpec.classBuilder("Outer") |
| .addOriginatingElement(outerElement) |
| .addType(TypeSpec.classBuilder("Inner") |
| .addOriginatingElement(innerElement) |
| .build()) |
| .build(); |
| assertThat(outer.originatingElements).containsExactly(outerElement, innerElement); |
| } |
| |
| @Test public void intersectionType() { |
| TypeVariableName typeVariable = TypeVariableName.get("T", Comparator.class, Serializable.class); |
| TypeSpec taco = TypeSpec.classBuilder("Taco") |
| .addMethod(MethodSpec.methodBuilder("getComparator") |
| .addTypeVariable(typeVariable) |
| .returns(typeVariable) |
| .addCode("return null;\n") |
| .build()) |
| .build(); |
| assertThat(toString(taco)).isEqualTo("" |
| + "package com.squareup.tacos;\n" |
| + "\n" |
| + "import java.io.Serializable;\n" |
| + "import java.util.Comparator;\n" |
| + "\n" |
| + "class Taco {\n" |
| + " <T extends Comparator & Serializable> T getComparator() {\n" |
| + " return null;\n" |
| + " }\n" |
| + "}\n"); |
| } |
| |
| @Test public void arrayType() { |
| TypeSpec taco = TypeSpec.classBuilder("Taco") |
| .addField(int[].class, "ints") |
| .build(); |
| assertThat(toString(taco)).isEqualTo("" |
| + "package com.squareup.tacos;\n" |
| + "\n" |
| + "class Taco {\n" |
| + " int[] ints;\n" |
| + "}\n"); |
| } |
| |
| @Test public void javadoc() { |
| TypeSpec taco = TypeSpec.classBuilder("Taco") |
| .addJavadoc("A hard or soft tortilla, loosely folded and filled with whatever {@link \n") |
| .addJavadoc("{@link $T random} tex-mex stuff we could find in the pantry\n", Random.class) |
| .addJavadoc(CodeBlock.of("and some {@link $T} cheese.\n", String.class)) |
| .addField(FieldSpec.builder(boolean.class, "soft") |
| .addJavadoc("True for a soft flour tortilla; false for a crunchy corn tortilla.\n") |
| .build()) |
| .addMethod(MethodSpec.methodBuilder("refold") |
| .addJavadoc("Folds the back of this taco to reduce sauce leakage.\n" |
| + "\n" |
| + "<p>For {@link $T#KOREAN}, the front may also be folded.\n", Locale.class) |
| .addParameter(Locale.class, "locale") |
| .build()) |
| .build(); |
| // Mentioning a type in Javadoc will not cause an import to be added (java.util.Random here), |
| // but the short name will be used if it's already imported (java.util.Locale here). |
| assertThat(toString(taco)).isEqualTo("" |
| + "package com.squareup.tacos;\n" |
| + "\n" |
| + "import java.util.Locale;\n" |
| + "\n" |
| + "/**\n" |
| + " * A hard or soft tortilla, loosely folded and filled with whatever {@link \n" |
| + " * {@link java.util.Random random} tex-mex stuff we could find in the pantry\n" |
| + " * and some {@link java.lang.String} cheese.\n" |
| + " */\n" |
| + "class Taco {\n" |
| + " /**\n" |
| + " * True for a soft flour tortilla; false for a crunchy corn tortilla.\n" |
| + " */\n" |
| + " boolean soft;\n" |
| + "\n" |
| + " /**\n" |
| + " * Folds the back of this taco to reduce sauce leakage.\n" |
| + " *\n" |
| + " * <p>For {@link Locale#KOREAN}, the front may also be folded.\n" |
| + " */\n" |
| + " void refold(Locale locale) {\n" |
| + " }\n" |
| + "}\n"); |
| } |
| |
| @Test public void annotationsInAnnotations() throws Exception { |
| ClassName beef = ClassName.get(tacosPackage, "Beef"); |
| ClassName chicken = ClassName.get(tacosPackage, "Chicken"); |
| ClassName option = ClassName.get(tacosPackage, "Option"); |
| ClassName mealDeal = ClassName.get(tacosPackage, "MealDeal"); |
| TypeSpec menu = TypeSpec.classBuilder("Menu") |
| .addAnnotation(AnnotationSpec.builder(mealDeal) |
| .addMember("price", "$L", 500) |
| .addMember("options", "$L", AnnotationSpec.builder(option) |
| .addMember("name", "$S", "taco") |
| .addMember("meat", "$T.class", beef) |
| .build()) |
| .addMember("options", "$L", AnnotationSpec.builder(option) |
| .addMember("name", "$S", "quesadilla") |
| .addMember("meat", "$T.class", chicken) |
| .build()) |
| .build()) |
| .build(); |
| assertThat(toString(menu)).isEqualTo("" |
| + "package com.squareup.tacos;\n" |
| + "\n" |
| + "@MealDeal(\n" |
| + " price = 500,\n" |
| + " options = {\n" |
| + " @Option(name = \"taco\", meat = Beef.class),\n" |
| + " @Option(name = \"quesadilla\", meat = Chicken.class)\n" |
| + " }\n" |
| + ")\n" |
| + "class Menu {\n" |
| + "}\n"); |
| } |
| |
| @Test public void varargs() throws Exception { |
| TypeSpec taqueria = TypeSpec.classBuilder("Taqueria") |
| .addMethod(MethodSpec.methodBuilder("prepare") |
| .addParameter(int.class, "workers") |
| .addParameter(Runnable[].class, "jobs") |
| .varargs() |
| .build()) |
| .build(); |
| assertThat(toString(taqueria)).isEqualTo("" |
| + "package com.squareup.tacos;\n" |
| + "\n" |
| + "import java.lang.Runnable;\n" |
| + "\n" |
| + "class Taqueria {\n" |
| + " void prepare(int workers, Runnable... jobs) {\n" |
| + " }\n" |
| + "}\n"); |
| } |
| |
| @Test public void codeBlocks() throws Exception { |
| CodeBlock ifBlock = CodeBlock.builder() |
| .beginControlFlow("if (!a.equals(b))") |
| .addStatement("return i") |
| .endControlFlow() |
| .build(); |
| CodeBlock methodBody = CodeBlock.builder() |
| .addStatement("$T size = $T.min(listA.size(), listB.size())", int.class, Math.class) |
| .beginControlFlow("for ($T i = 0; i < size; i++)", int.class) |
| .addStatement("$T $N = $N.get(i)", String.class, "a", "listA") |
| .addStatement("$T $N = $N.get(i)", String.class, "b", "listB") |
| .add("$L", ifBlock) |
| .endControlFlow() |
| .addStatement("return size") |
| .build(); |
| CodeBlock fieldBlock = CodeBlock.builder() |
| .add("$>$>") |
| .add("\n$T.<$T, $T>builder()$>$>", ImmutableMap.class, String.class, String.class) |
| .add("\n.add($S, $S)", '\'', "'") |
| .add("\n.add($S, $S)", '&', "&") |
| .add("\n.add($S, $S)", '<', "<") |
| .add("\n.add($S, $S)", '>', ">") |
| .add("\n.build()$<$<") |
| .add("$<$<") |
| .build(); |
| FieldSpec escapeHtml = FieldSpec.builder(ParameterizedTypeName.get( |
| Map.class, String.class, String.class), "ESCAPE_HTML") |
| .addModifiers(Modifier.PRIVATE, Modifier.STATIC, Modifier.FINAL) |
| .initializer(fieldBlock) |
| .build(); |
| TypeSpec util = TypeSpec.classBuilder("Util") |
| .addField(escapeHtml) |
| .addMethod(MethodSpec.methodBuilder("commonPrefixLength") |
| .returns(int.class) |
| .addParameter(ParameterizedTypeName.get(List.class, String.class), "listA") |
| .addParameter(ParameterizedTypeName.get(List.class, String.class), "listB") |
| .addCode(methodBody) |
| .build()) |
| .build(); |
| assertThat(toString(util)).isEqualTo("" |
| + "package com.squareup.tacos;\n" |
| + "\n" |
| + "import com.google.common.collect.ImmutableMap;\n" |
| + "import java.lang.Math;\n" |
| + "import java.lang.String;\n" |
| + "import java.util.List;\n" |
| + "import java.util.Map;\n" |
| + "\n" |
| + "class Util {\n" |
| + " private static final Map<String, String> ESCAPE_HTML = \n" |
| + " ImmutableMap.<String, String>builder()\n" |
| + " .add(\"\'\", \"'\")\n" |
| + " .add(\"&\", \"&\")\n" |
| + " .add(\"<\", \"<\")\n" |
| + " .add(\">\", \">\")\n" |
| + " .build();\n" |
| + "\n" |
| + " int commonPrefixLength(List<String> listA, List<String> listB) {\n" |
| + " int size = Math.min(listA.size(), listB.size());\n" |
| + " for (int i = 0; i < size; i++) {\n" |
| + " String a = listA.get(i);\n" |
| + " String b = listB.get(i);\n" |
| + " if (!a.equals(b)) {\n" |
| + " return i;\n" |
| + " }\n" |
| + " }\n" |
| + " return size;\n" |
| + " }\n" |
| + "}\n"); |
| } |
| |
| @Test public void indexedElseIf() throws Exception { |
| TypeSpec taco = TypeSpec.classBuilder("Taco") |
| .addMethod(MethodSpec.methodBuilder("choices") |
| .beginControlFlow("if ($1L != null || $1L == $2L)", "taco", "otherTaco") |
| .addStatement("$T.out.println($S)", System.class, "only one taco? NOO!") |
| .nextControlFlow("else if ($1L.$3L && $2L.$3L)", "taco", "otherTaco", "isSupreme()") |
| .addStatement("$T.out.println($S)", System.class, "taco heaven") |
| .endControlFlow() |
| .build()) |
| .build(); |
| assertThat(toString(taco)).isEqualTo("" |
| + "package com.squareup.tacos;\n" |
| + "\n" |
| + "import java.lang.System;\n" |
| + "\n" |
| + "class Taco {\n" |
| + " void choices() {\n" |
| + " if (taco != null || taco == otherTaco) {\n" |
| + " System.out.println(\"only one taco? NOO!\");\n" |
| + " } else if (taco.isSupreme() && otherTaco.isSupreme()) {\n" |
| + " System.out.println(\"taco heaven\");\n" |
| + " }\n" |
| + " }\n" |
| + "}\n"); |
| } |
| |
| @Test public void elseIf() throws Exception { |
| TypeSpec taco = TypeSpec.classBuilder("Taco") |
| .addMethod(MethodSpec.methodBuilder("choices") |
| .beginControlFlow("if (5 < 4) ") |
| .addStatement("$T.out.println($S)", System.class, "wat") |
| .nextControlFlow("else if (5 < 6)") |
| .addStatement("$T.out.println($S)", System.class, "hello") |
| .endControlFlow() |
| .build()) |
| .build(); |
| assertThat(toString(taco)).isEqualTo("" |
| + "package com.squareup.tacos;\n" |
| + "\n" |
| + "import java.lang.System;\n" |
| + "\n" |
| + "class Taco {\n" |
| + " void choices() {\n" |
| + " if (5 < 4) {\n" |
| + " System.out.println(\"wat\");\n" |
| + " } else if (5 < 6) {\n" |
| + " System.out.println(\"hello\");\n" |
| + " }\n" |
| + " }\n" |
| + "}\n"); |
| } |
| |
| @Test public void doWhile() throws Exception { |
| TypeSpec taco = TypeSpec.classBuilder("Taco") |
| .addMethod(MethodSpec.methodBuilder("loopForever") |
| .beginControlFlow("do") |
| .addStatement("$T.out.println($S)", System.class, "hello") |
| .endControlFlow("while (5 < 6)") |
| .build()) |
| .build(); |
| assertThat(toString(taco)).isEqualTo("" |
| + "package com.squareup.tacos;\n" |
| + "\n" |
| + "import java.lang.System;\n" |
| + "\n" |
| + "class Taco {\n" |
| + " void loopForever() {\n" |
| + " do {\n" |
| + " System.out.println(\"hello\");\n" |
| + " } while (5 < 6);\n" |
| + " }\n" |
| + "}\n"); |
| } |
| |
| @Test public void inlineIndent() throws Exception { |
| TypeSpec taco = TypeSpec.classBuilder("Taco") |
| .addMethod(MethodSpec.methodBuilder("inlineIndent") |
| .addCode("if (3 < 4) {\n$>$T.out.println($S);\n$<}\n", System.class, "hello") |
| .build()) |
| .build(); |
| assertThat(toString(taco)).isEqualTo("" |
| + "package com.squareup.tacos;\n" |
| + "\n" |
| + "import java.lang.System;\n" |
| + "\n" |
| + "class Taco {\n" |
| + " void inlineIndent() {\n" |
| + " if (3 < 4) {\n" |
| + " System.out.println(\"hello\");\n" |
| + " }\n" |
| + " }\n" |
| + "}\n"); |
| } |
| |
| @Test public void defaultModifiersForInterfaceMembers() throws Exception { |
| TypeSpec taco = TypeSpec.interfaceBuilder("Taco") |
| .addField(FieldSpec.builder(String.class, "SHELL") |
| .addModifiers(Modifier.PUBLIC, Modifier.STATIC, Modifier.FINAL) |
| .initializer("$S", "crunchy corn") |
| .build()) |
| .addMethod(MethodSpec.methodBuilder("fold") |
| .addModifiers(Modifier.PUBLIC, Modifier.ABSTRACT) |
| .build()) |
| .addType(TypeSpec.classBuilder("Topping") |
| .addModifiers(Modifier.PUBLIC, Modifier.STATIC) |
| .build()) |
| .build(); |
| assertThat(toString(taco)).isEqualTo("" |
| + "package com.squareup.tacos;\n" |
| + "\n" |
| + "import java.lang.String;\n" |
| + "\n" |
| + "interface Taco {\n" |
| + " String SHELL = \"crunchy corn\";\n" |
| + "\n" |
| + " void fold();\n" |
| + "\n" |
| + " class Topping {\n" |
| + " }\n" |
| + "}\n"); |
| } |
| |
| @Test public void defaultModifiersForMemberInterfacesAndEnums() throws Exception { |
| TypeSpec taco = TypeSpec.classBuilder("Taco") |
| .addType(TypeSpec.classBuilder("Meat") |
| .addModifiers(Modifier.STATIC) |
| .build()) |
| .addType(TypeSpec.interfaceBuilder("Tortilla") |
| .addModifiers(Modifier.STATIC) |
| .build()) |
| .addType(TypeSpec.enumBuilder("Topping") |
| .addModifiers(Modifier.STATIC) |
| .addEnumConstant("SALSA") |
| .build()) |
| .build(); |
| assertThat(toString(taco)).isEqualTo("" |
| + "package com.squareup.tacos;\n" |
| + "\n" |
| + "class Taco {\n" |
| + " static class Meat {\n" |
| + " }\n" |
| + "\n" |
| + " interface Tortilla {\n" |
| + " }\n" |
| + "\n" |
| + " enum Topping {\n" |
| + " SALSA\n" |
| + " }\n" |
| + "}\n"); |
| } |
| |
| @Test public void membersOrdering() throws Exception { |
| // Hand out names in reverse-alphabetical order to defend against unexpected sorting. |
| TypeSpec taco = TypeSpec.classBuilder("Members") |
| .addType(TypeSpec.classBuilder("Z").build()) |
| .addType(TypeSpec.classBuilder("Y").build()) |
| .addField(String.class, "X", Modifier.STATIC) |
| .addField(String.class, "W") |
| .addField(String.class, "V", Modifier.STATIC) |
| .addField(String.class, "U") |
| .addMethod(MethodSpec.methodBuilder("T").addModifiers(Modifier.STATIC).build()) |
| .addMethod(MethodSpec.methodBuilder("S").build()) |
| .addMethod(MethodSpec.methodBuilder("R").addModifiers(Modifier.STATIC).build()) |
| .addMethod(MethodSpec.methodBuilder("Q").build()) |
| .addMethod(MethodSpec.constructorBuilder().addParameter(int.class, "p").build()) |
| .addMethod(MethodSpec.constructorBuilder().addParameter(long.class, "o").build()) |
| .build(); |
| // Static fields, instance fields, constructors, methods, classes. |
| assertThat(toString(taco)).isEqualTo("" |
| + "package com.squareup.tacos;\n" |
| + "\n" |
| + "import java.lang.String;\n" |
| + "\n" |
| + "class Members {\n" |
| + " static String X;\n" |
| + "\n" |
| + " static String V;\n" |
| + "\n" |
| + " String W;\n" |
| + "\n" |
| + " String U;\n" |
| + "\n" |
| + " Members(int p) {\n" |
| + " }\n" |
| + "\n" |
| + " Members(long o) {\n" |
| + " }\n" |
| + "\n" |
| + " static void T() {\n" |
| + " }\n" |
| + "\n" |
| + " void S() {\n" |
| + " }\n" |
| + "\n" |
| + " static void R() {\n" |
| + " }\n" |
| + "\n" |
| + " void Q() {\n" |
| + " }\n" |
| + "\n" |
| + " class Z {\n" |
| + " }\n" |
| + "\n" |
| + " class Y {\n" |
| + " }\n" |
| + "}\n"); |
| } |
| |
| @Test public void nativeMethods() throws Exception { |
| TypeSpec taco = TypeSpec.classBuilder("Taco") |
| .addMethod(MethodSpec.methodBuilder("nativeInt") |
| .addModifiers(Modifier.NATIVE) |
| .returns(int.class) |
| .build()) |
| // GWT JSNI |
| .addMethod(MethodSpec.methodBuilder("alert") |
| .addModifiers(Modifier.PUBLIC, Modifier.STATIC, Modifier.NATIVE) |
| .addParameter(String.class, "msg") |
| .addCode(CodeBlock.builder() |
| .add(" /*-{\n") |
| .indent() |
| .addStatement("$$wnd.alert(msg)") |
| .unindent() |
| .add("}-*/") |
| .build()) |
| .build()) |
| .build(); |
| assertThat(toString(taco)).isEqualTo("" |
| + "package com.squareup.tacos;\n" |
| + "\n" |
| + "import java.lang.String;\n" |
| + "\n" |
| + "class Taco {\n" |
| + " native int nativeInt();\n" |
| + "\n" |
| + " public static native void alert(String msg) /*-{\n" |
| + " $wnd.alert(msg);\n" |
| + " }-*/;\n" |
| + "}\n"); |
| } |
| |
| @Test public void nullStringLiteral() throws Exception { |
| TypeSpec taco = TypeSpec.classBuilder("Taco") |
| .addField(FieldSpec.builder(String.class, "NULL") |
| .initializer("$S", (Object) null) |
| .build()) |
| .build(); |
| assertThat(toString(taco)).isEqualTo("" |
| + "package com.squareup.tacos;\n" |
| + "\n" |
| + "import java.lang.String;\n" |
| + "\n" |
| + "class Taco {\n" |
| + " String NULL = null;\n" |
| + "}\n"); |
| } |
| |
| @Test public void annotationToString() throws Exception { |
| AnnotationSpec annotation = AnnotationSpec.builder(SuppressWarnings.class) |
| .addMember("value", "$S", "unused") |
| .build(); |
| assertThat(annotation.toString()).isEqualTo("@java.lang.SuppressWarnings(\"unused\")"); |
| } |
| |
| @Test public void codeBlockToString() throws Exception { |
| CodeBlock codeBlock = CodeBlock.builder() |
| .addStatement("$T $N = $S.substring(0, 3)", String.class, "s", "taco") |
| .build(); |
| assertThat(codeBlock.toString()).isEqualTo("java.lang.String s = \"taco\".substring(0, 3);\n"); |
| } |
| |
| @Test public void codeBlockAddStatementOfCodeBlockToString() throws Exception { |
| CodeBlock contents = CodeBlock.of("$T $N = $S.substring(0, 3)", String.class, "s", "taco"); |
| CodeBlock statement = CodeBlock.builder().addStatement(contents).build(); |
| assertThat(statement.toString()).isEqualTo("java.lang.String s = \"taco\".substring(0, 3);\n"); |
| } |
| |
| @Test public void fieldToString() throws Exception { |
| FieldSpec field = FieldSpec.builder(String.class, "s", Modifier.FINAL) |
| .initializer("$S.substring(0, 3)", "taco") |
| .build(); |
| assertThat(field.toString()) |
| .isEqualTo("final java.lang.String s = \"taco\".substring(0, 3);\n"); |
| } |
| |
| @Test public void methodToString() throws Exception { |
| MethodSpec method = MethodSpec.methodBuilder("toString") |
| .addAnnotation(Override.class) |
| .addModifiers(Modifier.PUBLIC) |
| .returns(String.class) |
| .addStatement("return $S", "taco") |
| .build(); |
| assertThat(method.toString()).isEqualTo("" |
| + "@java.lang.Override\n" |
| + "public java.lang.String toString() {\n" |
| + " return \"taco\";\n" |
| + "}\n"); |
| } |
| |
| @Test public void constructorToString() throws Exception { |
| MethodSpec constructor = MethodSpec.constructorBuilder() |
| .addModifiers(Modifier.PUBLIC) |
| .addParameter(ClassName.get(tacosPackage, "Taco"), "taco") |
| .addStatement("this.$N = $N", "taco", "taco") |
| .build(); |
| assertThat(constructor.toString()).isEqualTo("" |
| + "public Constructor(com.squareup.tacos.Taco taco) {\n" |
| + " this.taco = taco;\n" |
| + "}\n"); |
| } |
| |
| @Test public void parameterToString() throws Exception { |
| ParameterSpec parameter = ParameterSpec.builder(ClassName.get(tacosPackage, "Taco"), "taco") |
| .addModifiers(Modifier.FINAL) |
| .addAnnotation(ClassName.get("javax.annotation", "Nullable")) |
| .build(); |
| assertThat(parameter.toString()) |
| .isEqualTo("@javax.annotation.Nullable final com.squareup.tacos.Taco taco"); |
| } |
| |
| @Test public void classToString() throws Exception { |
| TypeSpec type = TypeSpec.classBuilder("Taco") |
| .build(); |
| assertThat(type.toString()).isEqualTo("" |
| + "class Taco {\n" |
| + "}\n"); |
| } |
| |
| @Test public void anonymousClassToString() throws Exception { |
| TypeSpec type = TypeSpec.anonymousClassBuilder("") |
| .addSuperinterface(Runnable.class) |
| .addMethod(MethodSpec.methodBuilder("run") |
| .addAnnotation(Override.class) |
| .addModifiers(Modifier.PUBLIC) |
| .build()) |
| .build(); |
| assertThat(type.toString()).isEqualTo("" |
| + "new java.lang.Runnable() {\n" |
| + " @java.lang.Override\n" |
| + " public void run() {\n" |
| + " }\n" |
| + "}"); |
| } |
| |
| @Test public void interfaceClassToString() throws Exception { |
| TypeSpec type = TypeSpec.interfaceBuilder("Taco") |
| .build(); |
| assertThat(type.toString()).isEqualTo("" |
| + "interface Taco {\n" |
| + "}\n"); |
| } |
| |
| @Test public void annotationDeclarationToString() throws Exception { |
| TypeSpec type = TypeSpec.annotationBuilder("Taco") |
| .build(); |
| assertThat(type.toString()).isEqualTo("" |
| + "@interface Taco {\n" |
| + "}\n"); |
| } |
| |
| private String toString(TypeSpec typeSpec) { |
| return JavaFile.builder(tacosPackage, typeSpec).build().toString(); |
| } |
| |
| @Test public void multilineStatement() throws Exception { |
| TypeSpec taco = TypeSpec.classBuilder("Taco") |
| .addMethod(MethodSpec.methodBuilder("toString") |
| .addAnnotation(Override.class) |
| .addModifiers(Modifier.PUBLIC) |
| .returns(String.class) |
| .addStatement("return $S\n+ $S\n+ $S\n+ $S\n+ $S", |
| "Taco(", "beef,", "lettuce,", "cheese", ")") |
| .build()) |
| .build(); |
| assertThat(toString(taco)).isEqualTo("" |
| + "package com.squareup.tacos;\n" |
| + "\n" |
| + "import java.lang.Override;\n" |
| + "import java.lang.String;\n" |
| + "\n" |
| + "class Taco {\n" |
| + " @Override\n" |
| + " public String toString() {\n" |
| + " return \"Taco(\"\n" |
| + " + \"beef,\"\n" |
| + " + \"lettuce,\"\n" |
| + " + \"cheese\"\n" |
| + " + \")\";\n" |
| + " }\n" |
| + "}\n"); |
| } |
| |
| @Test public void multilineStatementWithAnonymousClass() throws Exception { |
| TypeName stringComparator = ParameterizedTypeName.get(Comparator.class, String.class); |
| TypeName listOfString = ParameterizedTypeName.get(List.class, String.class); |
| TypeSpec prefixComparator = TypeSpec.anonymousClassBuilder("") |
| .addSuperinterface(stringComparator) |
| .addMethod(MethodSpec.methodBuilder("compare") |
| .addAnnotation(Override.class) |
| .addModifiers(Modifier.PUBLIC) |
| .returns(int.class) |
| .addParameter(String.class, "a") |
| .addParameter(String.class, "b") |
| .addStatement("return a.substring(0, length)\n" |
| + ".compareTo(b.substring(0, length))") |
| .build()) |
| .build(); |
| TypeSpec taco = TypeSpec.classBuilder("Taco") |
| .addMethod(MethodSpec.methodBuilder("comparePrefix") |
| .returns(stringComparator) |
| .addParameter(int.class, "length", Modifier.FINAL) |
| .addStatement("return $L", prefixComparator) |
| .build()) |
| .addMethod(MethodSpec.methodBuilder("sortPrefix") |
| .addParameter(listOfString, "list") |
| .addParameter(int.class, "length", Modifier.FINAL) |
| .addStatement("$T.sort(\nlist,\n$L)", Collections.class, prefixComparator) |
| .build()) |
| .build(); |
| assertThat(toString(taco)).isEqualTo("" |
| + "package com.squareup.tacos;\n" |
| + "\n" |
| + "import java.lang.Override;\n" |
| + "import java.lang.String;\n" |
| + "import java.util.Collections;\n" |
| + "import java.util.Comparator;\n" |
| + "import java.util.List;\n" |
| + "\n" |
| + "class Taco {\n" |
| + " Comparator<String> comparePrefix(final int length) {\n" |
| + " return new Comparator<String>() {\n" |
| + " @Override\n" |
| + " public int compare(String a, String b) {\n" |
| + " return a.substring(0, length)\n" |
| + " .compareTo(b.substring(0, length));\n" |
| + " }\n" |
| + " };\n" |
| + " }\n" |
| + "\n" |
| + " void sortPrefix(List<String> list, final int length) {\n" |
| + " Collections.sort(\n" |
| + " list,\n" |
| + " new Comparator<String>() {\n" |
| + " @Override\n" |
| + " public int compare(String a, String b) {\n" |
| + " return a.substring(0, length)\n" |
| + " .compareTo(b.substring(0, length));\n" |
| + " }\n" |
| + " });\n" |
| + " }\n" |
| + "}\n"); |
| } |
| |
| @Test public void multilineStrings() throws Exception { |
| TypeSpec taco = TypeSpec.classBuilder("Taco") |
| .addField(FieldSpec.builder(String.class, "toppings") |
| .initializer("$S", "shell\nbeef\nlettuce\ncheese\n") |
| .build()) |
| .build(); |
| assertThat(toString(taco)).isEqualTo("" |
| + "package com.squareup.tacos;\n" |
| + "\n" |
| + "import java.lang.String;\n" |
| + "\n" |
| + "class Taco {\n" |
| + " String toppings = \"shell\\n\"\n" |
| + " + \"beef\\n\"\n" |
| + " + \"lettuce\\n\"\n" |
| + " + \"cheese\\n\";\n" |
| + "}\n"); |
| } |
| |
| @Test public void doubleFieldInitialization() { |
| try { |
| FieldSpec.builder(String.class, "listA") |
| .initializer("foo") |
| .initializer("bar") |
| .build(); |
| fail(); |
| } catch (IllegalStateException expected) { |
| } |
| |
| try { |
| FieldSpec.builder(String.class, "listA") |
| .initializer(CodeBlock.builder().add("foo").build()) |
| .initializer(CodeBlock.builder().add("bar").build()) |
| .build(); |
| fail(); |
| } catch (IllegalStateException expected) { |
| } |
| } |
| |
| @Test public void nullAnnotationsAddition() { |
| try { |
| TypeSpec.classBuilder("Taco").addAnnotations(null); |
| fail(); |
| } catch (IllegalArgumentException expected) { |
| assertThat(expected.getMessage()) |
| .isEqualTo("annotationSpecs == null"); |
| } |
| } |
| |
| @Test public void multipleAnnotationAddition() { |
| TypeSpec taco = TypeSpec.classBuilder("Taco") |
| .addAnnotations(Arrays.asList( |
| AnnotationSpec.builder(SuppressWarnings.class) |
| .addMember("value", "$S", "unchecked") |
| .build(), |
| AnnotationSpec.builder(Deprecated.class).build())) |
| .build(); |
| assertThat(toString(taco)).isEqualTo("" |
| + "package com.squareup.tacos;\n" |
| + "\n" |
| + "import java.lang.Deprecated;\n" |
| + "import java.lang.SuppressWarnings;\n" |
| + "\n" |
| + "@SuppressWarnings(\"unchecked\")\n" |
| + "@Deprecated\n" |
| + "class Taco {\n" |
| + "}\n"); |
| } |
| |
| @Test public void nullFieldsAddition() { |
| try { |
| TypeSpec.classBuilder("Taco").addFields(null); |
| fail(); |
| } catch (IllegalArgumentException expected) { |
| assertThat(expected.getMessage()) |
| .isEqualTo("fieldSpecs == null"); |
| } |
| } |
| |
| @Test public void multipleFieldAddition() { |
| TypeSpec taco = TypeSpec.classBuilder("Taco") |
| .addFields(Arrays.asList( |
| FieldSpec.builder(int.class, "ANSWER", Modifier.STATIC, Modifier.FINAL).build(), |
| FieldSpec.builder(BigDecimal.class, "price", Modifier.PRIVATE).build())) |
| .build(); |
| assertThat(toString(taco)).isEqualTo("" |
| + "package com.squareup.tacos;\n" |
| + "\n" |
| + "import java.math.BigDecimal;\n" |
| + "\n" |
| + "class Taco {\n" |
| + " static final int ANSWER;\n" |
| + "\n" |
| + " private BigDecimal price;\n" |
| + "}\n"); |
| } |
| |
| @Test public void nullMethodsAddition() { |
| try { |
| TypeSpec.classBuilder("Taco").addMethods(null); |
| fail(); |
| } catch (IllegalArgumentException expected) { |
| assertThat(expected.getMessage()) |
| .isEqualTo("methodSpecs == null"); |
| } |
| } |
| |
| @Test public void multipleMethodAddition() { |
| TypeSpec taco = TypeSpec.classBuilder("Taco") |
| .addMethods(Arrays.asList( |
| MethodSpec.methodBuilder("getAnswer") |
| .addModifiers(Modifier.PUBLIC, Modifier.STATIC) |
| .returns(int.class) |
| .addStatement("return $L", 42) |
| .build(), |
| MethodSpec.methodBuilder("getRandomQuantity") |
| .addModifiers(Modifier.PUBLIC) |
| .returns(int.class) |
| .addJavadoc("chosen by fair dice roll ;)") |
| .addStatement("return $L", 4) |
| .build())) |
| .build(); |
| assertThat(toString(taco)).isEqualTo("" |
| + "package com.squareup.tacos;\n" |
| + "\n" |
| + "class Taco {\n" |
| + " public static int getAnswer() {\n" |
| + " return 42;\n" |
| + " }\n" |
| + "\n" |
| + " /**\n" |
| + " * chosen by fair dice roll ;) */\n" |
| + " public int getRandomQuantity() {\n" |
| + " return 4;\n" |
| + " }\n" |
| + "}\n"); |
| } |
| |
| @Test public void nullSuperinterfacesAddition() { |
| try { |
| TypeSpec.classBuilder("Taco").addSuperinterfaces(null); |
| fail(); |
| } catch (IllegalArgumentException expected) { |
| assertThat(expected.getMessage()) |
| .isEqualTo("superinterfaces == null"); |
| } |
| } |
| |
| @Test public void nullSingleSuperinterfaceAddition() { |
| try { |
| TypeSpec.classBuilder("Taco").addSuperinterface((TypeName) null); |
| fail(); |
| } catch (IllegalArgumentException expected) { |
| assertThat(expected.getMessage()) |
| .isEqualTo("superinterface == null"); |
| } |
| } |
| |
| @Test public void nullInSuperinterfaceIterableAddition() { |
| List<TypeName> superinterfaces = new ArrayList<>(); |
| superinterfaces.add(TypeName.get(List.class)); |
| superinterfaces.add(null); |
| |
| try { |
| TypeSpec.classBuilder("Taco").addSuperinterfaces(superinterfaces); |
| fail(); |
| } catch (IllegalArgumentException expected) { |
| assertThat(expected.getMessage()) |
| .isEqualTo("superinterface == null"); |
| } |
| } |
| |
| @Test public void multipleSuperinterfaceAddition() { |
| TypeSpec taco = TypeSpec.classBuilder("Taco") |
| .addSuperinterfaces(Arrays.asList( |
| TypeName.get(Serializable.class), |
| TypeName.get(EventListener.class))) |
| .build(); |
| assertThat(toString(taco)).isEqualTo("" |
| + "package com.squareup.tacos;\n" |
| + "\n" |
| + "import java.io.Serializable;\n" |
| + "import java.util.EventListener;\n" |
| + "\n" |
| + "class Taco implements Serializable, EventListener {\n" |
| + "}\n"); |
| } |
| |
| @Test public void nullModifiersAddition() { |
| try { |
| TypeSpec.classBuilder("Taco").addModifiers((Modifier) null); |
| fail(); |
| } catch(IllegalArgumentException expected) { |
| assertThat(expected.getMessage()) |
| .isEqualTo("modifiers contain null"); |
| } |
| } |
| |
| @Test public void nullTypeVariablesAddition() { |
| try { |
| TypeSpec.classBuilder("Taco").addTypeVariables(null); |
| fail(); |
| } catch (IllegalArgumentException expected) { |
| assertThat(expected.getMessage()) |
| .isEqualTo("typeVariables == null"); |
| } |
| } |
| |
| @Test public void multipleTypeVariableAddition() { |
| TypeSpec location = TypeSpec.classBuilder("Location") |
| .addTypeVariables(Arrays.asList( |
| TypeVariableName.get("T"), |
| TypeVariableName.get("P", Number.class))) |
| .build(); |
| assertThat(toString(location)).isEqualTo("" |
| + "package com.squareup.tacos;\n" |
| + "\n" |
| + "import java.lang.Number;\n" |
| + "\n" |
| + "class Location<T, P extends Number> {\n" |
| + "}\n"); |
| } |
| |
| @Test public void nullTypesAddition() { |
| try { |
| TypeSpec.classBuilder("Taco").addTypes(null); |
| fail(); |
| } catch (IllegalArgumentException expected) { |
| assertThat(expected.getMessage()) |
| .isEqualTo("typeSpecs == null"); |
| } |
| } |
| |
| @Test public void multipleTypeAddition() { |
| TypeSpec taco = TypeSpec.classBuilder("Taco") |
| .addTypes(Arrays.asList( |
| TypeSpec.classBuilder("Topping").build(), |
| TypeSpec.classBuilder("Sauce").build())) |
| .build(); |
| assertThat(toString(taco)).isEqualTo("" |
| + "package com.squareup.tacos;\n" |
| + "\n" |
| + "class Taco {\n" |
| + " class Topping {\n" |
| + " }\n" |
| + "\n" |
| + " class Sauce {\n" |
| + " }\n" |
| + "}\n"); |
| } |
| |
| @Test public void tryCatch() { |
| TypeSpec taco = TypeSpec.classBuilder("Taco") |
| .addMethod(MethodSpec.methodBuilder("addTopping") |
| .addParameter(ClassName.get("com.squareup.tacos", "Topping"), "topping") |
| .beginControlFlow("try") |
| .addCode("/* do something tricky with the topping */\n") |
| .nextControlFlow("catch ($T e)", |
| ClassName.get("com.squareup.tacos", "IllegalToppingException")) |
| .endControlFlow() |
| .build()) |
| .build(); |
| assertThat(toString(taco)).isEqualTo("" |
| + "package com.squareup.tacos;\n" |
| + "\n" |
| + "class Taco {\n" |
| + " void addTopping(Topping topping) {\n" |
| + " try {\n" |
| + " /* do something tricky with the topping */\n" |
| + " } catch (IllegalToppingException e) {\n" |
| + " }\n" |
| + " }\n" |
| + "}\n"); |
| } |
| |
| @Test public void ifElse() { |
| TypeSpec taco = TypeSpec.classBuilder("Taco") |
| .addMethod( |
| MethodSpec.methodBuilder("isDelicious") |
| .addParameter(TypeName.INT, "count") |
| .returns(TypeName.BOOLEAN) |
| .beginControlFlow("if (count > 0)") |
| .addStatement("return true") |
| .nextControlFlow("else") |
| .addStatement("return false") |
| .endControlFlow() |
| .build() |
| ) |
| .build(); |
| assertThat(toString(taco)).isEqualTo("" |
| + "package com.squareup.tacos;\n" |
| + "\n" |
| + "class Taco {\n" |
| + " boolean isDelicious(int count) {\n" |
| + " if (count > 0) {\n" |
| + " return true;\n" |
| + " } else {\n" |
| + " return false;\n" |
| + " }\n" |
| + " }\n" |
| + "}\n"); |
| } |
| |
| @Test public void literalFromAnything() { |
| Object value = new Object() { |
| @Override public String toString() { |
| return "foo"; |
| } |
| }; |
| assertThat(CodeBlock.of("$L", value).toString()).isEqualTo("foo"); |
| } |
| |
| @Test public void nameFromCharSequence() { |
| assertThat(CodeBlock.of("$N", "text").toString()).isEqualTo("text"); |
| } |
| |
| @Test public void nameFromField() { |
| FieldSpec field = FieldSpec.builder(String.class, "field").build(); |
| assertThat(CodeBlock.of("$N", field).toString()).isEqualTo("field"); |
| } |
| |
| @Test public void nameFromParameter() { |
| ParameterSpec parameter = ParameterSpec.builder(String.class, "parameter").build(); |
| assertThat(CodeBlock.of("$N", parameter).toString()).isEqualTo("parameter"); |
| } |
| |
| @Test public void nameFromMethod() { |
| MethodSpec method = MethodSpec.methodBuilder("method") |
| .addModifiers(Modifier.ABSTRACT) |
| .returns(String.class) |
| .build(); |
| assertThat(CodeBlock.of("$N", method).toString()).isEqualTo("method"); |
| } |
| |
| @Test public void nameFromType() { |
| TypeSpec type = TypeSpec.classBuilder("Type").build(); |
| assertThat(CodeBlock.of("$N", type).toString()).isEqualTo("Type"); |
| } |
| |
| @Test public void nameFromUnsupportedType() { |
| try { |
| CodeBlock.builder().add("$N", String.class); |
| fail(); |
| } catch (IllegalArgumentException expected) { |
| assertThat(expected).hasMessageThat().isEqualTo("expected name but was " + String.class); |
| } |
| } |
| |
| @Test public void stringFromAnything() { |
| Object value = new Object() { |
| @Override public String toString() { |
| return "foo"; |
| } |
| }; |
| assertThat(CodeBlock.of("$S", value).toString()).isEqualTo("\"foo\""); |
| } |
| |
| @Test public void stringFromNull() { |
| assertThat(CodeBlock.of("$S", new Object[] {null}).toString()).isEqualTo("null"); |
| } |
| |
| @Test public void typeFromTypeName() { |
| TypeName typeName = TypeName.get(String.class); |
| assertThat(CodeBlock.of("$T", typeName).toString()).isEqualTo("java.lang.String"); |
| } |
| |
| @Test public void typeFromTypeMirror() { |
| TypeMirror mirror = getElement(String.class).asType(); |
| assertThat(CodeBlock.of("$T", mirror).toString()).isEqualTo("java.lang.String"); |
| } |
| |
| @Test public void typeFromTypeElement() { |
| TypeElement element = getElement(String.class); |
| assertThat(CodeBlock.of("$T", element).toString()).isEqualTo("java.lang.String"); |
| } |
| |
| @Test public void typeFromReflectType() { |
| assertThat(CodeBlock.of("$T", String.class).toString()).isEqualTo("java.lang.String"); |
| } |
| |
| @Test public void typeFromUnsupportedType() { |
| try { |
| CodeBlock.builder().add("$T", "java.lang.String"); |
| fail(); |
| } catch (IllegalArgumentException expected) { |
| assertThat(expected).hasMessageThat().isEqualTo("expected type but was java.lang.String"); |
| } |
| } |
| |
| @Test public void tooFewArguments() { |
| try { |
| CodeBlock.builder().add("$S"); |
| fail(); |
| } catch (IllegalArgumentException expected) { |
| assertThat(expected).hasMessageThat().isEqualTo("index 1 for '$S' not in range (received 0 arguments)"); |
| } |
| } |
| |
| @Test public void unusedArgumentsRelative() { |
| try { |
| CodeBlock.builder().add("$L $L", "a", "b", "c"); |
| fail(); |
| } catch (IllegalArgumentException expected) { |
| assertThat(expected).hasMessageThat().isEqualTo("unused arguments: expected 2, received 3"); |
| } |
| } |
| |
| @Test public void unusedArgumentsIndexed() { |
| try { |
| CodeBlock.builder().add("$1L $2L", "a", "b", "c"); |
| fail(); |
| } catch (IllegalArgumentException expected) { |
| assertThat(expected).hasMessageThat().isEqualTo("unused argument: $3"); |
| } |
| try { |
| CodeBlock.builder().add("$1L $1L $1L", "a", "b", "c"); |
| fail(); |
| } catch (IllegalArgumentException expected) { |
| assertThat(expected).hasMessageThat().isEqualTo("unused arguments: $2, $3"); |
| } |
| try { |
| CodeBlock.builder().add("$3L $1L $3L $1L $3L", "a", "b", "c", "d"); |
| fail(); |
| } catch (IllegalArgumentException expected) { |
| assertThat(expected).hasMessageThat().isEqualTo("unused arguments: $2, $4"); |
| } |
| } |
| |
| @Test public void superClassOnlyValidForClasses() { |
| try { |
| TypeSpec.annotationBuilder("A").superclass(ClassName.get(Object.class)); |
| fail(); |
| } catch (IllegalStateException expected) { |
| } |
| try { |
| TypeSpec.enumBuilder("E").superclass(ClassName.get(Object.class)); |
| fail(); |
| } catch (IllegalStateException expected) { |
| } |
| try { |
| TypeSpec.interfaceBuilder("I").superclass(ClassName.get(Object.class)); |
| fail(); |
| } catch (IllegalStateException expected) { |
| } |
| } |
| |
| @Test public void invalidSuperClass() { |
| try { |
| TypeSpec.classBuilder("foo") |
| .superclass(ClassName.get(List.class)) |
| .superclass(ClassName.get(Map.class)); |
| fail(); |
| } catch (IllegalStateException expected) { |
| } |
| try { |
| TypeSpec.classBuilder("foo") |
| .superclass(TypeName.INT); |
| fail(); |
| } catch (IllegalArgumentException expected) { |
| } |
| } |
| |
| @Test public void staticCodeBlock() { |
| TypeSpec taco = TypeSpec.classBuilder("Taco") |
| .addField(String.class, "foo", Modifier.PRIVATE) |
| .addField(String.class, "FOO", Modifier.PRIVATE, Modifier.STATIC, Modifier.FINAL) |
| .addStaticBlock(CodeBlock.builder() |
| .addStatement("FOO = $S", "FOO") |
| .build()) |
| .addMethod(MethodSpec.methodBuilder("toString") |
| .addAnnotation(Override.class) |
| .addModifiers(Modifier.PUBLIC) |
| .returns(String.class) |
| .addCode("return FOO;\n") |
| .build()) |
| .build(); |
| assertThat(toString(taco)).isEqualTo("" |
| + "package com.squareup.tacos;\n" |
| + "\n" |
| + "import java.lang.Override;\n" |
| + "import java.lang.String;\n" |
| + "\n" |
| + "class Taco {\n" |
| + " private static final String FOO;\n" |
| + "\n" |
| + " static {\n" |
| + " FOO = \"FOO\";\n" |
| + " }\n" |
| + "\n" |
| + " private String foo;\n" |
| + "\n" |
| + " @Override\n" |
| + " public String toString() {\n" |
| + " return FOO;\n" |
| + " }\n" |
| + "}\n"); |
| } |
| |
| @Test public void initializerBlockInRightPlace() { |
| TypeSpec taco = TypeSpec.classBuilder("Taco") |
| .addField(String.class, "foo", Modifier.PRIVATE) |
| .addField(String.class, "FOO", Modifier.PRIVATE, Modifier.STATIC, Modifier.FINAL) |
| .addStaticBlock(CodeBlock.builder() |
| .addStatement("FOO = $S", "FOO") |
| .build()) |
| .addMethod(MethodSpec.constructorBuilder().build()) |
| .addMethod(MethodSpec.methodBuilder("toString") |
| .addAnnotation(Override.class) |
| .addModifiers(Modifier.PUBLIC) |
| .returns(String.class) |
| .addCode("return FOO;\n") |
| .build()) |
| .addInitializerBlock(CodeBlock.builder() |
| .addStatement("foo = $S", "FOO") |
| .build()) |
| .build(); |
| assertThat(toString(taco)).isEqualTo("" |
| + "package com.squareup.tacos;\n" |
| + "\n" |
| + "import java.lang.Override;\n" |
| + "import java.lang.String;\n" |
| + "\n" |
| + "class Taco {\n" |
| + " private static final String FOO;\n" |
| + "\n" |
| + " static {\n" |
| + " FOO = \"FOO\";\n" |
| + " }\n" |
| + "\n" |
| + " private String foo;\n" |
| + "\n" |
| + " {\n" |
| + " foo = \"FOO\";\n" |
| + " }\n" |
| + "\n" |
| + " Taco() {\n" |
| + " }\n" |
| + "\n" |
| + " @Override\n" |
| + " public String toString() {\n" |
| + " return FOO;\n" |
| + " }\n" |
| + "}\n"); |
| } |
| |
| @Test public void initializersToBuilder() { |
| // Tests if toBuilder() contains correct static and instance initializers |
| TypeSpec taco = TypeSpec.classBuilder("Taco") |
| .addField(String.class, "foo", Modifier.PRIVATE) |
| .addField(String.class, "FOO", Modifier.PRIVATE, Modifier.STATIC, Modifier.FINAL) |
| .addStaticBlock(CodeBlock.builder() |
| .addStatement("FOO = $S", "FOO") |
| .build()) |
| .addMethod(MethodSpec.constructorBuilder().build()) |
| .addMethod(MethodSpec.methodBuilder("toString") |
| .addAnnotation(Override.class) |
| .addModifiers(Modifier.PUBLIC) |
| .returns(String.class) |
| .addCode("return FOO;\n") |
| .build()) |
| .addInitializerBlock(CodeBlock.builder() |
| .addStatement("foo = $S", "FOO") |
| .build()) |
| .build(); |
| |
| TypeSpec recreatedTaco = taco.toBuilder().build(); |
| assertThat(toString(taco)).isEqualTo(toString(recreatedTaco)); |
| |
| TypeSpec initializersAdded = taco.toBuilder() |
| .addInitializerBlock(CodeBlock.builder() |
| .addStatement("foo = $S", "instanceFoo") |
| .build()) |
| .addStaticBlock(CodeBlock.builder() |
| .addStatement("FOO = $S", "staticFoo") |
| .build()) |
| .build(); |
| |
| assertThat(toString(initializersAdded)).isEqualTo("" |
| + "package com.squareup.tacos;\n" |
| + "\n" |
| + "import java.lang.Override;\n" |
| + "import java.lang.String;\n" |
| + "\n" |
| + "class Taco {\n" |
| + " private static final String FOO;\n" |
| + "\n" |
| + " static {\n" |
| + " FOO = \"FOO\";\n" |
| + " }\n" |
| + " static {\n" |
| + " FOO = \"staticFoo\";\n" |
| + " }\n" |
| + "\n" |
| + " private String foo;\n" |
| + "\n" |
| + " {\n" |
| + " foo = \"FOO\";\n" |
| + " }\n" |
| + " {\n" |
| + " foo = \"instanceFoo\";\n" |
| + " }\n" |
| + "\n" |
| + " Taco() {\n" |
| + " }\n" |
| + "\n" |
| + " @Override\n" |
| + " public String toString() {\n" |
| + " return FOO;\n" |
| + " }\n" |
| + "}\n"); |
| } |
| |
| @Test public void initializerBlockUnsupportedExceptionOnInterface() { |
| TypeSpec.Builder interfaceBuilder = TypeSpec.interfaceBuilder("Taco"); |
| try { |
| interfaceBuilder.addInitializerBlock(CodeBlock.builder().build()); |
| fail("Exception expected"); |
| } catch (UnsupportedOperationException e) { |
| } |
| } |
| |
| @Test public void initializerBlockUnsupportedExceptionOnAnnotation() { |
| TypeSpec.Builder annotationBuilder = TypeSpec.annotationBuilder("Taco"); |
| try { |
| annotationBuilder.addInitializerBlock(CodeBlock.builder().build()); |
| fail("Exception expected"); |
| } catch (UnsupportedOperationException e) { |
| } |
| } |
| |
| @Test public void lineWrapping() { |
| MethodSpec.Builder methodBuilder = MethodSpec.methodBuilder("call"); |
| methodBuilder.addCode("$[call("); |
| for (int i = 0; i < 32; i++) { |
| methodBuilder.addParameter(String.class, "s" + i); |
| methodBuilder.addCode(i > 0 ? ",$W$S" : "$S", i); |
| } |
| methodBuilder.addCode(");$]\n"); |
| |
| TypeSpec taco = TypeSpec.classBuilder("Taco") |
| .addMethod(methodBuilder.build()) |
| .build(); |
| assertThat(toString(taco)).isEqualTo("" |
| + "package com.squareup.tacos;\n" |
| + "\n" |
| + "import java.lang.String;\n" |
| + "\n" |
| + "class Taco {\n" |
| + " void call(String s0, String s1, String s2, String s3, String s4, String s5, String s6, String s7,\n" |
| + " String s8, String s9, String s10, String s11, String s12, String s13, String s14, String s15,\n" |
| + " String s16, String s17, String s18, String s19, String s20, String s21, String s22,\n" |
| + " String s23, String s24, String s25, String s26, String s27, String s28, String s29,\n" |
| + " String s30, String s31) {\n" |
| + " call(\"0\", \"1\", \"2\", \"3\", \"4\", \"5\", \"6\", \"7\", \"8\", \"9\", \"10\", \"11\", \"12\", \"13\", \"14\", \"15\", \"16\",\n" |
| + " \"17\", \"18\", \"19\", \"20\", \"21\", \"22\", \"23\", \"24\", \"25\", \"26\", \"27\", \"28\", \"29\", \"30\", \"31\");\n" |
| + " }\n" |
| + "}\n"); |
| } |
| |
| @Test public void lineWrappingWithZeroWidthSpace() { |
| MethodSpec method = MethodSpec.methodBuilder("call") |
| .addCode("$[iAmSickOfWaitingInLine($Z") |
| .addCode("it, has, been, far, too, long, of, a, wait, and, i, would, like, to, eat, ") |
| .addCode("this, is, a, run, on, sentence") |
| .addCode(");$]\n") |
| .build(); |
| |
| TypeSpec taco = TypeSpec.classBuilder("Taco") |
| .addMethod(method) |
| .build(); |
| assertThat(toString(taco)).isEqualTo("" |
| + "package com.squareup.tacos;\n" |
| + "\n" |
| + "class Taco {\n" |
| + " void call() {\n" |
| + " iAmSickOfWaitingInLine(\n" |
| + " it, has, been, far, too, long, of, a, wait, and, i, would, like, to, eat, this, is, a, run, on, sentence);\n" |
| + " }\n" |
| + "}\n"); |
| } |
| |
| @Test public void equalsAndHashCode() { |
| TypeSpec a = TypeSpec.interfaceBuilder("taco").build(); |
| TypeSpec b = TypeSpec.interfaceBuilder("taco").build(); |
| assertThat(a.equals(b)).isTrue(); |
| assertThat(a.hashCode()).isEqualTo(b.hashCode()); |
| a = TypeSpec.classBuilder("taco").build(); |
| b = TypeSpec.classBuilder("taco").build(); |
| assertThat(a.equals(b)).isTrue(); |
| assertThat(a.hashCode()).isEqualTo(b.hashCode()); |
| a = TypeSpec.enumBuilder("taco").addEnumConstant("SALSA").build(); |
| b = TypeSpec.enumBuilder("taco").addEnumConstant("SALSA").build(); |
| assertThat(a.equals(b)).isTrue(); |
| assertThat(a.hashCode()).isEqualTo(b.hashCode()); |
| a = TypeSpec.annotationBuilder("taco").build(); |
| b = TypeSpec.annotationBuilder("taco").build(); |
| assertThat(a.equals(b)).isTrue(); |
| assertThat(a.hashCode()).isEqualTo(b.hashCode()); |
| } |
| |
| @Test public void classNameFactories() { |
| ClassName className = ClassName.get("com.example", "Example"); |
| assertThat(TypeSpec.classBuilder(className).build().name).isEqualTo("Example"); |
| assertThat(TypeSpec.interfaceBuilder(className).build().name).isEqualTo("Example"); |
| assertThat(TypeSpec.enumBuilder(className).addEnumConstant("A").build().name).isEqualTo("Example"); |
| assertThat(TypeSpec.annotationBuilder(className).build().name).isEqualTo("Example"); |
| } |
| } |