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, ClassName castTo)115 public static CodeBlock cast(CodeBlock expression, ClassName castTo) { 116 return CodeBlock.of("($T) $L", castTo, expression); 117 } 118 119 /** Returns {@code expression} cast to a type. */ cast(CodeBlock expression, Class<?> castTo)120 public static CodeBlock cast(CodeBlock expression, Class<?> castTo) { 121 return CodeBlock.of("($T) $L", castTo, expression); 122 } 123 type(TypeMirror type)124 public static CodeBlock type(TypeMirror type) { 125 return CodeBlock.of("$T", type); 126 } 127 stringLiteral(String toWrap)128 public static CodeBlock stringLiteral(String toWrap) { 129 return CodeBlock.of("$S", toWrap); 130 } 131 132 /** Returns a javadoc {@literal @link} tag that poins to the given {@link ExecutableElement}. */ javadocLinkTo(ExecutableElement executableElement)133 public static CodeBlock javadocLinkTo(ExecutableElement executableElement) { 134 CodeBlock.Builder builder = 135 CodeBlock.builder() 136 .add( 137 "{@link $T#", 138 rawTypeName( 139 ClassName.get(MoreElements.asType(executableElement.getEnclosingElement())))); 140 switch (executableElement.getKind()) { 141 case METHOD: 142 builder.add("$L", executableElement.getSimpleName()); 143 break; 144 case CONSTRUCTOR: 145 builder.add("$L", executableElement.getEnclosingElement().getSimpleName()); 146 break; 147 case STATIC_INIT: 148 case INSTANCE_INIT: 149 throw new IllegalArgumentException( 150 "cannot create a javadoc link to an initializer: " + executableElement); 151 default: 152 throw new AssertionError(executableElement.toString()); 153 } 154 builder.add("("); 155 builder.add( 156 executableElement.getParameters().stream() 157 .map(parameter -> CodeBlock.of("$T", rawTypeName(TypeName.get(parameter.asType())))) 158 .collect(toParametersCodeBlock())); 159 return builder.add(")}").build(); 160 } 161 CodeBlocks()162 private CodeBlocks() {} 163 } 164