1 /* 2 * Copyright (C) 2016 The Dagger Authors. 3 * 4 * Licensed under the Apache License, Version 2.0 (the "License"); 5 * you may not use this file except in compliance with the License. 6 * You may obtain a copy of the License at 7 * 8 * http://www.apache.org/licenses/LICENSE-2.0 9 * 10 * Unless required by applicable law or agreed to in writing, software 11 * distributed under the License is distributed on an "AS IS" BASIS, 12 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 * See the License for the specific language governing permissions and 14 * limitations under the License. 15 */ 16 17 package dagger.internal.codegen.javapoet; 18 19 import static com.squareup.javapoet.MethodSpec.methodBuilder; 20 import static com.squareup.javapoet.TypeSpec.anonymousClassBuilder; 21 import static dagger.internal.codegen.javapoet.TypeNames.providerOf; 22 import static dagger.internal.codegen.javapoet.TypeNames.rawTypeName; 23 import static java.util.stream.StreamSupport.stream; 24 import static javax.lang.model.element.Modifier.PUBLIC; 25 26 import com.google.auto.common.MoreElements; 27 import com.google.auto.common.MoreTypes; 28 import com.squareup.javapoet.ClassName; 29 import com.squareup.javapoet.CodeBlock; 30 import com.squareup.javapoet.MethodSpec; 31 import com.squareup.javapoet.ParameterSpec; 32 import com.squareup.javapoet.TypeName; 33 import java.util.stream.Collector; 34 import javax.lang.model.element.ExecutableElement; 35 import javax.lang.model.type.DeclaredType; 36 import javax.lang.model.type.TypeMirror; 37 38 /** Convenience methods for creating {@link CodeBlock}s. */ 39 public final class CodeBlocks { 40 /** 41 * Joins {@link CodeBlock} instances in a manner suitable for use as method parameters (or 42 * arguments). 43 */ toParametersCodeBlock()44 public static Collector<CodeBlock, ?, CodeBlock> toParametersCodeBlock() { 45 // TODO(ronshapiro,jakew): consider adding zero-width spaces to help line breaking when the 46 // formatter is off. If not, inline this 47 return CodeBlock.joining(", "); 48 } 49 50 /** Concatenates {@link CodeBlock} instances separated by newlines for readability. */ toConcatenatedCodeBlock()51 public static Collector<CodeBlock, ?, CodeBlock> toConcatenatedCodeBlock() { 52 return CodeBlock.joining("\n", "", "\n"); 53 } 54 55 /** Returns a comma-separated version of {@code codeBlocks} as one unified {@link CodeBlock}. */ makeParametersCodeBlock(Iterable<CodeBlock> codeBlocks)56 public static CodeBlock makeParametersCodeBlock(Iterable<CodeBlock> codeBlocks) { 57 return stream(codeBlocks.spliterator(), false).collect(toParametersCodeBlock()); 58 } 59 60 /** 61 * Returns a comma-separated {@link CodeBlock} using the name of every parameter in {@code 62 * parameters}. 63 */ parameterNames(Iterable<ParameterSpec> parameters)64 public static CodeBlock parameterNames(Iterable<ParameterSpec> parameters) { 65 // TODO(ronshapiro): Add DaggerStreams.stream(Iterable) 66 return stream(parameters.spliterator(), false) 67 .map(p -> CodeBlock.of("$N", p)) 68 .collect(toParametersCodeBlock()); 69 } 70 71 /** 72 * Returns one unified {@link CodeBlock} which joins each item in {@code codeBlocks} with a 73 * newline. 74 */ concat(Iterable<CodeBlock> codeBlocks)75 public static CodeBlock concat(Iterable<CodeBlock> codeBlocks) { 76 return stream(codeBlocks.spliterator(), false).collect(toConcatenatedCodeBlock()); 77 } 78 79 /** Adds an annotation to a method. */ addAnnotation(MethodSpec.Builder method, DeclaredType nullableType)80 public static void addAnnotation(MethodSpec.Builder method, DeclaredType nullableType) { 81 method.addAnnotation(ClassName.get(MoreTypes.asTypeElement(nullableType))); 82 } 83 84 /** 85 * Returns an anonymous {@link javax.inject.Provider} class with the single {@link 86 * javax.inject.Provider#get()} method that returns the given {@code expression}. 87 */ anonymousProvider(Expression expression)88 public static CodeBlock anonymousProvider(Expression expression) { 89 // More of a precondition check that the type Provider is parameterized with is a DeclaredType 90 DeclaredType type = MoreTypes.asDeclared(expression.type()); 91 return anonymousProvider( 92 TypeName.get(type), CodeBlock.of("return $L;", expression.codeBlock())); 93 } 94 95 /** 96 * Returns an anonymous {@link javax.inject.Provider} class with the single {@link 97 * javax.inject.Provider#get()} method implemented by {@code body}. 98 */ anonymousProvider(TypeName providedType, CodeBlock body)99 public static CodeBlock anonymousProvider(TypeName providedType, CodeBlock body) { 100 return CodeBlock.of( 101 "$L", 102 anonymousClassBuilder("") 103 .superclass(providerOf(providedType)) 104 .addMethod( 105 methodBuilder("get") 106 .addAnnotation(Override.class) 107 .addModifiers(PUBLIC) 108 .returns(providedType) 109 .addCode(body) 110 .build()) 111 .build()); 112 } 113 114 /** Returns {@code expression} cast to a type. */ cast(CodeBlock expression, Class<?> castTo)115 public static CodeBlock cast(CodeBlock expression, Class<?> castTo) { 116 return CodeBlock.of("($T) $L", castTo, expression); 117 } 118 type(TypeMirror type)119 public static CodeBlock type(TypeMirror type) { 120 return CodeBlock.of("$T", type); 121 } 122 stringLiteral(String toWrap)123 public static CodeBlock stringLiteral(String toWrap) { 124 return CodeBlock.of("$S", toWrap); 125 } 126 127 /** Returns a javadoc {@literal @link} tag that poins to the given {@link ExecutableElement}. */ javadocLinkTo(ExecutableElement executableElement)128 public static CodeBlock javadocLinkTo(ExecutableElement executableElement) { 129 CodeBlock.Builder builder = 130 CodeBlock.builder() 131 .add( 132 "{@link $T#", 133 rawTypeName( 134 ClassName.get(MoreElements.asType(executableElement.getEnclosingElement())))); 135 switch (executableElement.getKind()) { 136 case METHOD: 137 builder.add("$L", executableElement.getSimpleName()); 138 break; 139 case CONSTRUCTOR: 140 builder.add("$L", executableElement.getEnclosingElement().getSimpleName()); 141 break; 142 case STATIC_INIT: 143 case INSTANCE_INIT: 144 throw new IllegalArgumentException( 145 "cannot create a javadoc link to an initializer: " + executableElement); 146 default: 147 throw new AssertionError(executableElement.toString()); 148 } 149 builder.add("("); 150 builder.add( 151 executableElement.getParameters().stream() 152 .map(parameter -> CodeBlock.of("$T", rawTypeName(TypeName.get(parameter.asType())))) 153 .collect(toParametersCodeBlock())); 154 return builder.add(")}").build(); 155 } 156 CodeBlocks()157 private CodeBlocks() {} 158 } 159