blob: c8f06405c887ae18c7bb1ce341db7ef38d264ec6 [file] [log] [blame]
ronshapiro732e5022016-01-07 12:10:55 -08001/*
ronshapiro5dde42d2016-06-17 09:03:35 -07002 * Copyright (C) 2016 The Dagger Authors.
ronshapiro732e5022016-01-07 12:10:55 -08003 *
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 */
dpb1b65b6a2016-07-11 12:11:24 -070016
ronshapiro732e5022016-01-07 12:10:55 -080017package dagger.internal.codegen;
18
ronshapiro57f302c2017-02-01 11:35:47 -080019import static dagger.internal.codegen.TypeNames.rawTypeName;
gak1ab10de2017-01-06 12:57:33 -080020import static java.util.stream.StreamSupport.stream;
21
ronshapiro57f302c2017-02-01 11:35:47 -080022import com.google.auto.common.MoreElements;
gak1ab10de2017-01-06 12:57:33 -080023import com.google.errorprone.annotations.CanIgnoreReturnValue;
ronshapiro57f302c2017-02-01 11:35:47 -080024import com.squareup.javapoet.ClassName;
ronshapiro732e5022016-01-07 12:10:55 -080025import com.squareup.javapoet.CodeBlock;
gak1ab10de2017-01-06 12:57:33 -080026import com.squareup.javapoet.CodeBlock.Builder;
gak00a6fb12016-11-14 23:33:06 -080027import com.squareup.javapoet.TypeName;
gak1ab10de2017-01-06 12:57:33 -080028import java.util.stream.Collector;
gak00a6fb12016-11-14 23:33:06 -080029import javax.lang.model.element.ExecutableElement;
30import javax.lang.model.element.VariableElement;
ronshapiro732e5022016-01-07 12:10:55 -080031
32final class CodeBlocks {
gak1ab10de2017-01-06 12:57:33 -080033 /**
34 * A {@link Collector} implementation that joins {@link CodeBlock} instances together into one
35 * separated by {@code delimiter}. For example, joining {@code String s}, {@code Object o} and
36 * {@code int i} using {@code ", "} would produce {@code String s, Object o, int i}.
37 */
38 static Collector<CodeBlock, ?, CodeBlock> joiningCodeBlocks(String delimiter) {
39 return Collector.of(
40 () -> new CodeBlockJoiner(delimiter, CodeBlock.builder()),
41 CodeBlockJoiner::add,
42 CodeBlockJoiner::merge,
43 CodeBlockJoiner::join);
44 }
45
46 /**
47 * Joins {@link CodeBlock} instances in a manner suitable for use as method parameters (or
48 * arguments). This is equivalent to {@code joiningCodeBlocks(", ")}.
49 */
50 static Collector<CodeBlock, ?, CodeBlock> toParametersCodeBlock() {
51 return joiningCodeBlocks(", ");
52 }
53
54 /**
55 * Joins {@link TypeName} instances into a {@link CodeBlock} that is a comma-separated list for
56 * use as type parameters or javadoc method arguments.
57 */
58 static Collector<TypeName, ?, CodeBlock> toTypeNamesCodeBlock() {
gak1ab10de2017-01-06 12:57:33 -080059 return Collector.of(
cushonf5447bc2017-03-01 18:35:39 -080060 () -> new CodeBlockJoiner(", ", CodeBlock.builder()),
gak1ab10de2017-01-06 12:57:33 -080061 CodeBlockJoiner::addTypeName,
62 CodeBlockJoiner::merge,
63 CodeBlockJoiner::join);
64 }
65
66 /**
67 * Concatenates {@link CodeBlock} instances separated by newlines for readability. This is
68 * equivalent to {@code joiningCodeBlocks("\n")}.
69 */
70 static Collector<CodeBlock, ?, CodeBlock> toConcatenatedCodeBlock() {
71 return joiningCodeBlocks("\n");
72 }
ronshapiro732e5022016-01-07 12:10:55 -080073
gak00a6fb12016-11-14 23:33:06 -080074 /** Returns a comma-separated version of {@code codeBlocks} as one unified {@link CodeBlock}. */
ronshapiro732e5022016-01-07 12:10:55 -080075 static CodeBlock makeParametersCodeBlock(Iterable<CodeBlock> codeBlocks) {
gak1ab10de2017-01-06 12:57:33 -080076 return stream(codeBlocks.spliterator(), false).collect(toParametersCodeBlock());
77 }
78
79 private static final class CodeBlockJoiner {
80 private final String delimiter;
81 private final CodeBlock.Builder builder;
82 private boolean first = true;
83
84 CodeBlockJoiner(String delimiter, Builder builder) {
85 this.delimiter = delimiter;
86 this.builder = builder;
87 }
88
89 @CanIgnoreReturnValue
90 CodeBlockJoiner add(CodeBlock codeBlock) {
91 maybeAddDelimiter();
92 builder.add(codeBlock);
93 return this;
94 }
95
96 @CanIgnoreReturnValue
97 CodeBlockJoiner addTypeName(TypeName typeName) {
98 maybeAddDelimiter();
99 builder.add("$T", typeName);
100 return this;
101 }
102
103 private void maybeAddDelimiter() {
104 if (!first) {
105 builder.add(delimiter);
106 }
107 first = false;
108 }
109
110 @CanIgnoreReturnValue
111 CodeBlockJoiner merge(CodeBlockJoiner other) {
112 CodeBlock otherBlock = other.builder.build();
113 if (!otherBlock.isEmpty()) {
114 add(otherBlock);
115 }
116 return this;
117 }
118
119 CodeBlock join() {
120 return builder.build();
121 }
ronshapiro732e5022016-01-07 12:10:55 -0800122 }
123
ronshapirof22babc2016-01-29 12:16:16 -0800124 /**
125 * Returns one unified {@link CodeBlock} which joins each item in {@code codeBlocks} with a
126 * newline.
127 */
128 static CodeBlock concat(Iterable<CodeBlock> codeBlocks) {
gak1ab10de2017-01-06 12:57:33 -0800129 return stream(codeBlocks.spliterator(), false).collect(toConcatenatedCodeBlock());
ronshapirof22babc2016-01-29 12:16:16 -0800130 }
131
ronshapiro6475f472016-02-02 07:03:10 -0800132 static CodeBlock stringLiteral(String toWrap) {
ronshapirob9158d82016-04-13 11:00:56 -0700133 return CodeBlock.of("$S", toWrap);
ronshapiro6475f472016-02-02 07:03:10 -0800134 }
135
gak00a6fb12016-11-14 23:33:06 -0800136 /** Returns a javadoc {@literal @link} tag that poins to the given {@link ExecutableElement}. */
137 static CodeBlock javadocLinkTo(ExecutableElement executableElement) {
138 CodeBlock.Builder builder =
ronshapiro57f302c2017-02-01 11:35:47 -0800139 CodeBlock.builder()
140 .add(
141 "{@link $T#",
142 rawTypeName(
143 ClassName.get(MoreElements.asType(executableElement.getEnclosingElement()))));
gak00a6fb12016-11-14 23:33:06 -0800144 switch (executableElement.getKind()) {
145 case METHOD:
146 builder.add("$L", executableElement.getSimpleName());
147 break;
148 case CONSTRUCTOR:
149 builder.add("$L", executableElement.getEnclosingElement().getSimpleName());
150 break;
151 case STATIC_INIT:
152 case INSTANCE_INIT:
153 throw new IllegalArgumentException(
154 "cannot create a javadoc link to an initializer: " + executableElement);
155 default:
156 throw new AssertionError(executableElement.toString());
157 }
158 builder.add("(");
cushonf5447bc2017-03-01 18:35:39 -0800159 builder.add(
160 executableElement
161 .getParameters()
162 .stream()
163 .map(VariableElement::asType)
164 .map(TypeName::get)
165 .map(TypeNames::rawTypeName)
166 .collect(toTypeNamesCodeBlock()));
gak00a6fb12016-11-14 23:33:06 -0800167 return builder.add(")}").build();
168 }
169
ronshapiro732e5022016-01-07 12:10:55 -0800170 private CodeBlocks() {}
171}