| ronshapiro | 732e502 | 2016-01-07 12:10:55 -0800 | [diff] [blame] | 1 | /* |
| ronshapiro | 5dde42d | 2016-06-17 09:03:35 -0700 | [diff] [blame] | 2 | * Copyright (C) 2016 The Dagger Authors. |
| ronshapiro | 732e502 | 2016-01-07 12:10:55 -0800 | [diff] [blame] | 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 | */ |
| dpb | 1b65b6a | 2016-07-11 12:11:24 -0700 | [diff] [blame] | 16 | |
| ronshapiro | 732e502 | 2016-01-07 12:10:55 -0800 | [diff] [blame] | 17 | package dagger.internal.codegen; |
| 18 | |
| ronshapiro | 57f302c | 2017-02-01 11:35:47 -0800 | [diff] [blame] | 19 | import static dagger.internal.codegen.TypeNames.rawTypeName; |
| gak | 1ab10de | 2017-01-06 12:57:33 -0800 | [diff] [blame] | 20 | import static java.util.stream.StreamSupport.stream; |
| 21 | |
| ronshapiro | 57f302c | 2017-02-01 11:35:47 -0800 | [diff] [blame] | 22 | import com.google.auto.common.MoreElements; |
| dpb | c8c6c05 | 2017-07-28 10:48:06 -0700 | [diff] [blame^] | 23 | import com.google.auto.common.MoreTypes; |
| gak | 1ab10de | 2017-01-06 12:57:33 -0800 | [diff] [blame] | 24 | import com.google.errorprone.annotations.CanIgnoreReturnValue; |
| ronshapiro | 57f302c | 2017-02-01 11:35:47 -0800 | [diff] [blame] | 25 | import com.squareup.javapoet.ClassName; |
| ronshapiro | 732e502 | 2016-01-07 12:10:55 -0800 | [diff] [blame] | 26 | import com.squareup.javapoet.CodeBlock; |
| gak | 1ab10de | 2017-01-06 12:57:33 -0800 | [diff] [blame] | 27 | import com.squareup.javapoet.CodeBlock.Builder; |
| dpb | c8c6c05 | 2017-07-28 10:48:06 -0700 | [diff] [blame^] | 28 | import com.squareup.javapoet.MethodSpec; |
| gak | 00a6fb1 | 2016-11-14 23:33:06 -0800 | [diff] [blame] | 29 | import com.squareup.javapoet.TypeName; |
| gak | 1ab10de | 2017-01-06 12:57:33 -0800 | [diff] [blame] | 30 | import java.util.stream.Collector; |
| gak | 00a6fb1 | 2016-11-14 23:33:06 -0800 | [diff] [blame] | 31 | import javax.lang.model.element.ExecutableElement; |
| 32 | import javax.lang.model.element.VariableElement; |
| dpb | c8c6c05 | 2017-07-28 10:48:06 -0700 | [diff] [blame^] | 33 | import javax.lang.model.type.DeclaredType; |
| ronshapiro | 732e502 | 2016-01-07 12:10:55 -0800 | [diff] [blame] | 34 | |
| 35 | final class CodeBlocks { |
| gak | 1ab10de | 2017-01-06 12:57:33 -0800 | [diff] [blame] | 36 | /** |
| 37 | * A {@link Collector} implementation that joins {@link CodeBlock} instances together into one |
| 38 | * separated by {@code delimiter}. For example, joining {@code String s}, {@code Object o} and |
| 39 | * {@code int i} using {@code ", "} would produce {@code String s, Object o, int i}. |
| 40 | */ |
| 41 | static Collector<CodeBlock, ?, CodeBlock> joiningCodeBlocks(String delimiter) { |
| 42 | return Collector.of( |
| 43 | () -> new CodeBlockJoiner(delimiter, CodeBlock.builder()), |
| 44 | CodeBlockJoiner::add, |
| 45 | CodeBlockJoiner::merge, |
| 46 | CodeBlockJoiner::join); |
| 47 | } |
| 48 | |
| 49 | /** |
| 50 | * Joins {@link CodeBlock} instances in a manner suitable for use as method parameters (or |
| 51 | * arguments). This is equivalent to {@code joiningCodeBlocks(", ")}. |
| 52 | */ |
| 53 | static Collector<CodeBlock, ?, CodeBlock> toParametersCodeBlock() { |
| 54 | return joiningCodeBlocks(", "); |
| 55 | } |
| 56 | |
| 57 | /** |
| 58 | * Joins {@link TypeName} instances into a {@link CodeBlock} that is a comma-separated list for |
| 59 | * use as type parameters or javadoc method arguments. |
| 60 | */ |
| 61 | static Collector<TypeName, ?, CodeBlock> toTypeNamesCodeBlock() { |
| gak | 1ab10de | 2017-01-06 12:57:33 -0800 | [diff] [blame] | 62 | return Collector.of( |
| cushon | f5447bc | 2017-03-01 18:35:39 -0800 | [diff] [blame] | 63 | () -> new CodeBlockJoiner(", ", CodeBlock.builder()), |
| gak | 1ab10de | 2017-01-06 12:57:33 -0800 | [diff] [blame] | 64 | CodeBlockJoiner::addTypeName, |
| 65 | CodeBlockJoiner::merge, |
| 66 | CodeBlockJoiner::join); |
| 67 | } |
| 68 | |
| 69 | /** |
| 70 | * Concatenates {@link CodeBlock} instances separated by newlines for readability. This is |
| 71 | * equivalent to {@code joiningCodeBlocks("\n")}. |
| 72 | */ |
| 73 | static Collector<CodeBlock, ?, CodeBlock> toConcatenatedCodeBlock() { |
| 74 | return joiningCodeBlocks("\n"); |
| 75 | } |
| ronshapiro | 732e502 | 2016-01-07 12:10:55 -0800 | [diff] [blame] | 76 | |
| gak | 00a6fb1 | 2016-11-14 23:33:06 -0800 | [diff] [blame] | 77 | /** Returns a comma-separated version of {@code codeBlocks} as one unified {@link CodeBlock}. */ |
| ronshapiro | 732e502 | 2016-01-07 12:10:55 -0800 | [diff] [blame] | 78 | static CodeBlock makeParametersCodeBlock(Iterable<CodeBlock> codeBlocks) { |
| gak | 1ab10de | 2017-01-06 12:57:33 -0800 | [diff] [blame] | 79 | return stream(codeBlocks.spliterator(), false).collect(toParametersCodeBlock()); |
| 80 | } |
| 81 | |
| dpb | c8c6c05 | 2017-07-28 10:48:06 -0700 | [diff] [blame^] | 82 | /** Adds an annotation to a method. */ |
| 83 | static void addAnnotation(MethodSpec.Builder method, DeclaredType nullableType) { |
| 84 | method.addAnnotation(ClassName.get(MoreTypes.asTypeElement(nullableType))); |
| 85 | } |
| 86 | |
| gak | 1ab10de | 2017-01-06 12:57:33 -0800 | [diff] [blame] | 87 | private static final class CodeBlockJoiner { |
| 88 | private final String delimiter; |
| 89 | private final CodeBlock.Builder builder; |
| 90 | private boolean first = true; |
| 91 | |
| 92 | CodeBlockJoiner(String delimiter, Builder builder) { |
| 93 | this.delimiter = delimiter; |
| 94 | this.builder = builder; |
| 95 | } |
| 96 | |
| 97 | @CanIgnoreReturnValue |
| 98 | CodeBlockJoiner add(CodeBlock codeBlock) { |
| 99 | maybeAddDelimiter(); |
| 100 | builder.add(codeBlock); |
| 101 | return this; |
| 102 | } |
| 103 | |
| 104 | @CanIgnoreReturnValue |
| 105 | CodeBlockJoiner addTypeName(TypeName typeName) { |
| 106 | maybeAddDelimiter(); |
| 107 | builder.add("$T", typeName); |
| 108 | return this; |
| 109 | } |
| 110 | |
| 111 | private void maybeAddDelimiter() { |
| 112 | if (!first) { |
| 113 | builder.add(delimiter); |
| 114 | } |
| 115 | first = false; |
| 116 | } |
| 117 | |
| 118 | @CanIgnoreReturnValue |
| 119 | CodeBlockJoiner merge(CodeBlockJoiner other) { |
| 120 | CodeBlock otherBlock = other.builder.build(); |
| 121 | if (!otherBlock.isEmpty()) { |
| 122 | add(otherBlock); |
| 123 | } |
| 124 | return this; |
| 125 | } |
| 126 | |
| 127 | CodeBlock join() { |
| 128 | return builder.build(); |
| 129 | } |
| ronshapiro | 732e502 | 2016-01-07 12:10:55 -0800 | [diff] [blame] | 130 | } |
| 131 | |
| ronshapiro | f22babc | 2016-01-29 12:16:16 -0800 | [diff] [blame] | 132 | /** |
| 133 | * Returns one unified {@link CodeBlock} which joins each item in {@code codeBlocks} with a |
| 134 | * newline. |
| 135 | */ |
| 136 | static CodeBlock concat(Iterable<CodeBlock> codeBlocks) { |
| gak | 1ab10de | 2017-01-06 12:57:33 -0800 | [diff] [blame] | 137 | return stream(codeBlocks.spliterator(), false).collect(toConcatenatedCodeBlock()); |
| ronshapiro | f22babc | 2016-01-29 12:16:16 -0800 | [diff] [blame] | 138 | } |
| 139 | |
| ronshapiro | 6475f47 | 2016-02-02 07:03:10 -0800 | [diff] [blame] | 140 | static CodeBlock stringLiteral(String toWrap) { |
| ronshapiro | b9158d8 | 2016-04-13 11:00:56 -0700 | [diff] [blame] | 141 | return CodeBlock.of("$S", toWrap); |
| ronshapiro | 6475f47 | 2016-02-02 07:03:10 -0800 | [diff] [blame] | 142 | } |
| 143 | |
| gak | 00a6fb1 | 2016-11-14 23:33:06 -0800 | [diff] [blame] | 144 | /** Returns a javadoc {@literal @link} tag that poins to the given {@link ExecutableElement}. */ |
| 145 | static CodeBlock javadocLinkTo(ExecutableElement executableElement) { |
| 146 | CodeBlock.Builder builder = |
| ronshapiro | 57f302c | 2017-02-01 11:35:47 -0800 | [diff] [blame] | 147 | CodeBlock.builder() |
| 148 | .add( |
| 149 | "{@link $T#", |
| 150 | rawTypeName( |
| 151 | ClassName.get(MoreElements.asType(executableElement.getEnclosingElement())))); |
| gak | 00a6fb1 | 2016-11-14 23:33:06 -0800 | [diff] [blame] | 152 | switch (executableElement.getKind()) { |
| 153 | case METHOD: |
| 154 | builder.add("$L", executableElement.getSimpleName()); |
| 155 | break; |
| 156 | case CONSTRUCTOR: |
| 157 | builder.add("$L", executableElement.getEnclosingElement().getSimpleName()); |
| 158 | break; |
| 159 | case STATIC_INIT: |
| 160 | case INSTANCE_INIT: |
| 161 | throw new IllegalArgumentException( |
| 162 | "cannot create a javadoc link to an initializer: " + executableElement); |
| 163 | default: |
| 164 | throw new AssertionError(executableElement.toString()); |
| 165 | } |
| 166 | builder.add("("); |
| cushon | f5447bc | 2017-03-01 18:35:39 -0800 | [diff] [blame] | 167 | builder.add( |
| 168 | executableElement |
| 169 | .getParameters() |
| 170 | .stream() |
| 171 | .map(VariableElement::asType) |
| 172 | .map(TypeName::get) |
| 173 | .map(TypeNames::rawTypeName) |
| 174 | .collect(toTypeNamesCodeBlock())); |
| gak | 00a6fb1 | 2016-11-14 23:33:06 -0800 | [diff] [blame] | 175 | return builder.add(")}").build(); |
| 176 | } |
| 177 | |
| ronshapiro | 732e502 | 2016-01-07 12:10:55 -0800 | [diff] [blame] | 178 | private CodeBlocks() {} |
| 179 | } |