• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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