diff options
author | Jake Wharton <JakeWharton@GMail.com> | 2015-01-07 11:21:31 -0800 |
---|---|---|
committer | Jake Wharton <JakeWharton@GMail.com> | 2015-01-07 11:21:31 -0800 |
commit | 21a955d166aacbcaa2f969b12c54778cca9172ca (patch) | |
tree | 20afa3b8ea86ea69911530b28e7d3d04e7a3f526 | |
parent | 12b3d2a3b4ddf036b5f2fced0d23b381909ee382 (diff) | |
parent | d7b2189e9e91cad8f2fec9b5d5dd3b85ef6ce6b9 (diff) | |
download | javapoet-21a955d166aacbcaa2f969b12c54778cca9172ca.tar.gz |
Merge pull request #152 from square/jwilson_0105_violence
First, rough draft of immutable builders.
10 files changed, 726 insertions, 1 deletions
diff --git a/checkstyle.xml b/checkstyle.xml index 675e2f2..ad56010 100644 --- a/checkstyle.xml +++ b/checkstyle.xml @@ -83,7 +83,7 @@ <module name="AvoidNestedBlocks"/> <!--module name="EmptyBlock"/--> <module name="LeftCurly"/> - <module name="NeedBraces"/> + <!--<module name="NeedBraces"/>--> <module name="RightCurly"/> diff --git a/src/main/java/com/squareup/javawriter/builders/CodeWriter.java b/src/main/java/com/squareup/javawriter/builders/CodeWriter.java new file mode 100644 index 0000000..005b666 --- /dev/null +++ b/src/main/java/com/squareup/javawriter/builders/CodeWriter.java @@ -0,0 +1,168 @@ +/* + * 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.javawriter.builders; + +import com.google.common.collect.ImmutableMap; +import com.google.common.collect.ImmutableSortedMap; +import com.squareup.javawriter.ClassName; +import com.squareup.javawriter.StringLiteral; +import com.squareup.javawriter.TypeName; +import com.squareup.javawriter.TypeNames; +import java.util.ArrayList; +import java.util.LinkedHashMap; +import java.util.LinkedHashSet; +import java.util.List; +import java.util.Map; + +import static com.google.common.base.Preconditions.checkNotNull; +import static com.google.common.base.Preconditions.checkState; + +/** + * Converts a {@link JavaFile} to a string suitable to both human- and javac-consumption. This + * honors imports, indentation, and deferred variable names. + */ +class CodeWriter { + private final String indent = " "; + private final StringBuilder out; + private final ImmutableMap<ClassName, String> importedTypes; + private final LinkedHashSet<TypeName> emittedTypes = new LinkedHashSet<>(); + private final List<TypeName> visibleTypes = new ArrayList<>(); + private int indentLevel; + + public CodeWriter(StringBuilder out, ImmutableMap<ClassName, String> importedTypes) { + this.out = checkNotNull(out); + this.importedTypes = checkNotNull(importedTypes); + } + + public CodeWriter indent() { + indentLevel++; + return this; + } + + public CodeWriter unindent() { + checkState(indentLevel > 0); + indentLevel--; + return this; + } + + public CodeWriter pushVisibleType(TypeName typeName) { + visibleTypes.add(typeName); + return this; + } + + public CodeWriter popVisibleType(TypeName typeName) { + checkState(visibleTypes.remove(typeName)); + return this; + } + + public CodeWriter emit(String format, Object... args) { + return emit(new Snippet(format, args)); + } + + public CodeWriter emit(Snippet snippet) { + int a = 0; + for (String part : snippet.formatParts) { + switch (part) { + case "$L": + emitAndIndent(String.valueOf(snippet.args.get(a++))); + break; + + case "$S": + String arg = String.valueOf(snippet.args.get(a++)); + emitAndIndent(StringLiteral.forValue(arg).literal()); + break; + + case "$T": + emitType(snippet.args.get(a++)); + break; + + case "$$": + emitAndIndent("$"); + break; + + default: + emitAndIndent(part); + break; + } + } + return this; + } + + private void emitType(Object arg) { + TypeName typeName = toTypeName(arg); + emittedTypes.add(typeName); + + String shortName = !visibleTypes.contains(typeName) + ? importedTypes.get(typeName) + : null; + + emitAndIndent(shortName != null ? shortName : typeName.toString()); + } + + /** Emits {@code s} with indentation as required. */ + private void emitAndIndent(String s) { + boolean first = true; + for (String line : s.split("\n", -1)) { + if (!first) out.append('\n'); + first = false; + if (line.isEmpty()) continue; // Don't indent empty lines. + emitIndentationIfNecessary(); + out.append(line); + } + } + + private void emitIndentationIfNecessary() { + // Only emit indentation immediately after a '\n' character. + if (out.length() <= 0 || out.charAt(out.length() - 1) != '\n') return; + + for (int j = 0; j < indentLevel; j++) { + out.append(indent); + } + } + + private TypeName toTypeName(Object arg) { + if (arg instanceof TypeName) return (TypeName) arg; + if (arg instanceof Class<?>) return TypeNames.forClass((Class<?>) arg); + throw new IllegalArgumentException("Expected type but was " + arg); + } + + /** + * Returns the types that should have been imported for this code. If there were any simple name + * collisions, that type's first use is imported. + */ + ImmutableMap<ClassName, String> suggestedImports() { + // Find the simple names that can be imported, and the classes that they target. + Map<String, ClassName> simpleNameToType = new LinkedHashMap<>(); + for (TypeName typeName : emittedTypes) { + if (!(typeName instanceof ClassName)) continue; + ClassName className = (ClassName) typeName; + if (simpleNameToType.containsKey(className.simpleName())) continue; + simpleNameToType.put(className.simpleName(), className); + } + + // Invert the map. + ImmutableSortedMap.Builder<ClassName, String> typeToSimpleName + = ImmutableSortedMap.naturalOrder(); + for (Map.Entry<String, ClassName> entry : simpleNameToType.entrySet()) { + typeToSimpleName.put(entry.getValue(), entry.getKey()); + } + + // TODO(jwilson): omit imports from java.lang, unless their simple names is also present in the + // current class's package. (Yuck.) + + return typeToSimpleName.build(); + } +} diff --git a/src/main/java/com/squareup/javawriter/builders/FieldSpec.java b/src/main/java/com/squareup/javawriter/builders/FieldSpec.java new file mode 100644 index 0000000..20322d8 --- /dev/null +++ b/src/main/java/com/squareup/javawriter/builders/FieldSpec.java @@ -0,0 +1,58 @@ +/* + * 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.javawriter.builders; + +import com.squareup.javawriter.TypeName; + +import static com.google.common.base.Preconditions.checkNotNull; + +/** A generated field declaration. */ +public final class FieldSpec { + public final TypeName type; + public final Name name; + public final Snippet initializer; + + private FieldSpec(Builder builder) { + this.type = checkNotNull(builder.type); + this.name = checkNotNull(builder.name); + this.initializer = checkNotNull(builder.initializer); + } + + public static final class Builder { + private TypeName type; + private Name name; + private Snippet initializer; + + public Builder type(TypeName type) { + this.type = type; + return this; + } + + public Builder name(Name name) { + this.name = name; + return this; + } + + public Builder initializer(String format, Object... args) { + this.initializer = new Snippet(format, args); + return this; + } + + public FieldSpec build() { + return new FieldSpec(this); + } + } +} diff --git a/src/main/java/com/squareup/javawriter/builders/JavaFile.java b/src/main/java/com/squareup/javawriter/builders/JavaFile.java new file mode 100644 index 0000000..59dbdec --- /dev/null +++ b/src/main/java/com/squareup/javawriter/builders/JavaFile.java @@ -0,0 +1,70 @@ +/* + * 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.javawriter.builders; + +import com.google.common.collect.ImmutableMap; +import com.squareup.javawriter.ClassName; + +import static com.google.common.base.Preconditions.checkArgument; +import static com.google.common.base.Preconditions.checkNotNull; + +/** A Java file containing a single top level class. */ +public final class JavaFile { + public final TypeSpec typeSpec; + + private JavaFile(Builder builder) { + this.typeSpec = checkNotNull(builder.typeSpec); + } + + public String toString() { + // First pass: emit the entire class, just to collect the types we'll need to import. + ImmutableMap<ClassName, String> noImports = ImmutableMap.of(); + CodeWriter importsCollector = new CodeWriter(new StringBuilder(), noImports); + emit(noImports, importsCollector); + ImmutableMap<ClassName, String> suggestedImports = importsCollector.suggestedImports(); + + // Second pass: Write the code, taking advantage of the imports. + StringBuilder result = new StringBuilder(); + CodeWriter codeWriter = new CodeWriter(result, suggestedImports); + emit(suggestedImports, codeWriter); + return result.toString(); + } + + private void emit(ImmutableMap<ClassName, String> imports, CodeWriter codeWriter) { + codeWriter.emit("package $L;\n", typeSpec.name.packageName()); + codeWriter.emit("\n"); + for (ClassName className : imports.keySet()) { + codeWriter.emit("import $L;\n", className); + } + codeWriter.emit("\n"); + typeSpec.emit(codeWriter); + } + + public static final class Builder { + private TypeSpec typeSpec; + + public Builder classSpec(TypeSpec typeSpec) { + checkArgument(!typeSpec.name.enclosingClassName().isPresent(), + "Cannot create a JavaFile for %s", typeSpec.name); + this.typeSpec = typeSpec; + return this; + } + + public JavaFile build() { + return new JavaFile(this); + } + } +} diff --git a/src/main/java/com/squareup/javawriter/builders/MethodSpec.java b/src/main/java/com/squareup/javawriter/builders/MethodSpec.java new file mode 100644 index 0000000..4d0a24b --- /dev/null +++ b/src/main/java/com/squareup/javawriter/builders/MethodSpec.java @@ -0,0 +1,121 @@ +/* + * 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.javawriter.builders; + +import com.google.common.collect.ImmutableList; +import com.google.common.collect.ImmutableSet; +import com.squareup.javawriter.ClassName; +import com.squareup.javawriter.TypeName; +import com.squareup.javawriter.VoidName; +import java.lang.annotation.Annotation; +import java.util.ArrayList; +import java.util.Arrays; +import java.util.List; +import javax.lang.model.element.Modifier; + +import static com.google.common.base.Preconditions.checkNotNull; + +/** A generated method declaration. */ +public final class MethodSpec { + public final Name name; + public final ImmutableList<ClassName> annotations; + public final ImmutableList<ParameterSpec> parameters; + public final ImmutableSet<Modifier> modifiers; + public final TypeName returnType; + public final ImmutableList<Snippet> snippets; + + private MethodSpec(Builder builder) { + this.name = checkNotNull(builder.name); + this.annotations = ImmutableList.copyOf(builder.annotations); + this.parameters = ImmutableList.copyOf(builder.parameters); + this.modifiers = ImmutableSet.copyOf(builder.modifiers); + this.returnType = builder.returnType; + this.snippets = ImmutableList.copyOf(builder.snippets); + } + + void emit(CodeWriter codeWriter) { + codeWriter.emit("$T $L(", returnType, name); // TODO(jwilson): modifiers. + + boolean firstParameter = true; + for (ParameterSpec parameterSpec : parameters) { + if (!firstParameter) codeWriter.emit(", "); + codeWriter.emit("$T $L", parameterSpec.type, parameterSpec.name); + firstParameter = false; + } + codeWriter.emit(") {\n"); + + codeWriter.indent(); + for (Snippet snippet : snippets) { + codeWriter.emit(snippet); + } + codeWriter.unindent(); + + codeWriter.emit("}\n"); + } + + public static final class Builder { + private Name name; + private List<ClassName> annotations = new ArrayList<>(); + private List<ParameterSpec> parameters = new ArrayList<>(); + private List<Modifier> modifiers = new ArrayList<>(); + private TypeName returnType = VoidName.VOID; + private List<Snippet> snippets = new ArrayList<>(); + + public Builder name(String name) { + this.name = new Name(name); + return this; + } + + public Builder name(Name name) { + this.name = name; + return this; + } + + public Builder addAnnotation(Class<? extends Annotation> annotation) { + this.annotations.add(ClassName.fromClass(annotation)); + return this; + } + + public Builder addParameter(ParameterSpec parameterSpec) { + this.parameters.add(parameterSpec); + return this; + } + + public Builder addParameter(Class<?> type, String name) { + this.parameters.add(new ParameterSpec.Builder().type(type).name(name).build()); + return this; + } + + public Builder addModifiers(Modifier... modifiers) { + this.modifiers.addAll(Arrays.asList(modifiers)); + return this; + } + + public Builder returns(Class<?> returnType) { + this.returnType = ClassName.fromClass(returnType); + return this; + } + + public Builder addCode(String format, Object... args) { + snippets.add(new Snippet(format, args)); + return this; + } + + public MethodSpec build() { + return new MethodSpec(this); + } + } +} diff --git a/src/main/java/com/squareup/javawriter/builders/Name.java b/src/main/java/com/squareup/javawriter/builders/Name.java new file mode 100644 index 0000000..36d1fb2 --- /dev/null +++ b/src/main/java/com/squareup/javawriter/builders/Name.java @@ -0,0 +1,36 @@ +/* + * 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.javawriter.builders; + +import static com.google.common.base.Preconditions.checkNotNull; + +/** + * A member name. If necessary, the seed name will be mangled to cope with keyword collision and + * name collision. For example, given the seed name {@code public}, the generated code may use + * {@code public_} or {@code public1}. + */ +public final class Name { + public final String seed; + + public Name(String seed) { + this.seed = checkNotNull(seed); + } + + @Override public String toString() { + // TODO(jwilson): implement deferred naming so that `new Name("public")` yields "public_" etc. + return seed; + } +} diff --git a/src/main/java/com/squareup/javawriter/builders/ParameterSpec.java b/src/main/java/com/squareup/javawriter/builders/ParameterSpec.java new file mode 100644 index 0000000..2db9ae7 --- /dev/null +++ b/src/main/java/com/squareup/javawriter/builders/ParameterSpec.java @@ -0,0 +1,61 @@ +/* + * 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.javawriter.builders; + +import com.squareup.javawriter.TypeName; +import com.squareup.javawriter.TypeNames; + +import static com.google.common.base.Preconditions.checkNotNull; + +/** A generated parameter declaration. */ +public final class ParameterSpec { + public final TypeName type; + public final Name name; + + private ParameterSpec(Builder builder) { + this.type = checkNotNull(builder.type); + this.name = checkNotNull(builder.name); + } + + public static final class Builder { + private TypeName type; + private Name name; + + public Builder type(TypeName type) { + this.type = type; + return this; + } + + public Builder type(Class<?> type) { + this.type = TypeNames.forClass(type); + return this; + } + + public Builder name(String name) { + this.name = new Name(name); + return this; + } + + public Builder name(Name name) { + this.name = name; + return this; + } + + public ParameterSpec build() { + return new ParameterSpec(this); + } + } +} diff --git a/src/main/java/com/squareup/javawriter/builders/Snippet.java b/src/main/java/com/squareup/javawriter/builders/Snippet.java new file mode 100644 index 0000000..39aa695 --- /dev/null +++ b/src/main/java/com/squareup/javawriter/builders/Snippet.java @@ -0,0 +1,73 @@ +/* + * 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.javawriter.builders; + +import com.google.common.collect.ImmutableList; + +import static com.google.common.base.Preconditions.checkArgument; +import static com.google.common.base.Preconditions.checkState; + +/** + * A deferred format string. Unlike {@link java.text.Format} which uses percent {@code %} to escape + * placeholders, this uses {@code $}, and has its own set of permitted placeholders: + * + * <ul> + * <li>{@code $L} emits the <em>literal</em> value with no escaping. + * <li>{@code $S} escapes the value as a <em>string</em>, wraps it with double quotes, and emits + * that. + * <li>{@code $T} emits a <em>type</em> reference. Types will be imported if possible. + * <li>{@code $$} emits a dollar sign. + * </ul> + */ +final class Snippet { + /** A heterogeneous list containing string literals and value placeholders. */ + final ImmutableList<String> formatParts; + final ImmutableList<Object> args; + + public Snippet(String format, Object[] args) { + ImmutableList.Builder<String> formatPartsBuilder = ImmutableList.builder(); + int expectedArgsLength = 0; + for (int p = 0, nextP; p < format.length(); p = nextP) { + if (format.charAt(p) != '$') { + nextP = format.indexOf('$', p + 1); + if (nextP == -1) nextP = format.length(); + } else { + checkState(p + 1 < format.length(), "dangling $ in format string %s", format); + switch (format.charAt(p + 1)) { + case 'L': + case 'S': + case 'T': + expectedArgsLength++; + // Fall through. + case '$': + nextP = p + 2; + break; + + default: + throw new IllegalArgumentException("invalid format string: " + format); + } + } + + formatPartsBuilder.add(format.substring(p, nextP)); + } + + checkArgument(args.length == expectedArgsLength, + "expected %s args but was %s", expectedArgsLength, args); + + this.formatParts = formatPartsBuilder.build(); + this.args = ImmutableList.copyOf(args); + } +} diff --git a/src/main/java/com/squareup/javawriter/builders/TypeSpec.java b/src/main/java/com/squareup/javawriter/builders/TypeSpec.java new file mode 100644 index 0000000..7cd0d80 --- /dev/null +++ b/src/main/java/com/squareup/javawriter/builders/TypeSpec.java @@ -0,0 +1,80 @@ +/* + * 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.javawriter.builders; + +import com.google.common.collect.ImmutableList; +import com.squareup.javawriter.ClassName; +import java.util.ArrayList; +import java.util.List; + +import static com.google.common.base.Preconditions.checkNotNull; + +/** A generated class, interface, or enum declaration. */ +public final class TypeSpec { + public final Type type; + public final ClassName name; + public final ImmutableList<MethodSpec> methodSpecs; + + private TypeSpec(Builder builder) { + this.type = checkNotNull(builder.type); + this.name = checkNotNull(builder.name); + this.methodSpecs = ImmutableList.copyOf(builder.methodSpecs); + } + + void emit(CodeWriter codeWriter) { + codeWriter.emit("class $L {\n", name.simpleName()); // TODO(jwilson): modifiers. + codeWriter.indent(); + + boolean firstMethod = true; + for (MethodSpec methodSpec : methodSpecs) { + if (!firstMethod) codeWriter.emit("\n"); + methodSpec.emit(codeWriter); + firstMethod = false; + } + + codeWriter.unindent(); + codeWriter.emit("}\n"); + } + + public static enum Type { + CLASS, INTERFACE, ENUM + } + + public static final class Builder { + private Type type = Type.CLASS; + private ClassName name; + private List<MethodSpec> methodSpecs = new ArrayList<>(); + + public Builder type(Type type) { + this.type = type; + return this; + } + + public Builder name(ClassName name) { + this.name = name; + return this; + } + + public Builder addMethod(MethodSpec methodSpec) { + methodSpecs.add(methodSpec); + return this; + } + + public TypeSpec build() { + return new TypeSpec(this); + } + } +} diff --git a/src/test/java/com/squareup/javawriter/builders/TypeSpecTest.java b/src/test/java/com/squareup/javawriter/builders/TypeSpecTest.java new file mode 100644 index 0000000..8fd68be --- /dev/null +++ b/src/test/java/com/squareup/javawriter/builders/TypeSpecTest.java @@ -0,0 +1,58 @@ +/* + * 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.javawriter.builders; + +import com.squareup.javawriter.ClassName; +import javax.lang.model.element.Modifier; +import org.junit.Test; + +import static com.google.common.truth.Truth.assertThat; + +public class TypeSpecTest { + @Test public void test() throws Exception { + TypeSpec taco = new TypeSpec.Builder() + .name(ClassName.create("com.squareup.tacos", "Taco")) + .addMethod(new MethodSpec.Builder() + .name("toString") + .addAnnotation(Override.class) + .addModifiers(Modifier.PUBLIC, Modifier.FINAL) + .returns(String.class) + .addCode("return $S;\n", "taco") + .build()) + .build(); + + // TODO: fix modifiers + // TODO: fix annotations + + assertThat(toString(taco)).isEqualTo("" + + "package com.squareup.tacos;\n" + + "\n" + + "import java.lang.String;\n" + + "\n" + + "class Taco {\n" + + " String toString() {\n" + + " return \"taco\";\n" + + " }\n" + + "}\n"); + } + + private String toString(TypeSpec typeSpec) { + return new JavaFile.Builder() + .classSpec(typeSpec) + .build() + .toString(); + } +} |