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.base; 18 19 import static com.google.common.base.Preconditions.checkArgument; 20 import static dagger.internal.codegen.extension.DaggerStreams.toImmutableMap; 21 import static dagger.internal.codegen.extension.DaggerStreams.valuesOf; 22 import static dagger.internal.codegen.xprocessing.XTypes.isDeclared; 23 24 import androidx.room.compiler.processing.XType; 25 import androidx.room.compiler.processing.XTypeElement; 26 import com.google.auto.value.AutoValue; 27 import com.google.common.collect.ImmutableMap; 28 import com.squareup.javapoet.ClassName; 29 import com.squareup.javapoet.CodeBlock; 30 import com.squareup.javapoet.ParameterizedTypeName; 31 import com.squareup.javapoet.TypeName; 32 import dagger.internal.codegen.javapoet.TypeNames; 33 import dagger.spi.model.Key; 34 import javax.lang.model.type.TypeMirror; 35 36 /** 37 * Information about an {@code Optional} {@link TypeMirror}. 38 * 39 * <p>{@link com.google.common.base.Optional} and {@link java.util.Optional} are supported. 40 */ 41 @AutoValue 42 public abstract class OptionalType { 43 private XType type; 44 45 /** A variant of {@code Optional}. */ 46 public enum OptionalKind { 47 /** {@link com.google.common.base.Optional}. */ 48 GUAVA_OPTIONAL(TypeNames.GUAVA_OPTIONAL, "absent"), 49 50 /** {@link java.util.Optional}. */ 51 JDK_OPTIONAL(TypeNames.JDK_OPTIONAL, "empty"); 52 53 // Keep a cache from class name to OptionalKind for quick look-up. 54 private static final ImmutableMap<ClassName, OptionalKind> OPTIONAL_KIND_BY_CLASS_NAME = 55 valuesOf(OptionalKind.class) 56 .collect(toImmutableMap(value -> value.className, value -> value)); 57 58 private final ClassName className; 59 private final String absentMethodName; 60 OptionalKind(ClassName className, String absentMethodName)61 OptionalKind(ClassName className, String absentMethodName) { 62 this.className = className; 63 this.absentMethodName = absentMethodName; 64 } 65 isOptionalKind(XTypeElement type)66 private static boolean isOptionalKind(XTypeElement type) { 67 return OPTIONAL_KIND_BY_CLASS_NAME.containsKey(type.getClassName()); 68 } 69 of(XTypeElement type)70 private static OptionalKind of(XTypeElement type) { 71 return OPTIONAL_KIND_BY_CLASS_NAME.get(type.getClassName()); 72 } 73 74 /** Returns {@code valueType} wrapped in the correct class. */ of(TypeName valueType)75 public ParameterizedTypeName of(TypeName valueType) { 76 return ParameterizedTypeName.get(className, valueType); 77 } 78 79 /** Returns an expression for the absent/empty value. */ absentValueExpression()80 public CodeBlock absentValueExpression() { 81 return CodeBlock.of("$T.$L()", className, absentMethodName); 82 } 83 84 /** 85 * Returns an expression for the absent/empty value, parameterized with {@link #valueType()}. 86 */ parameterizedAbsentValueExpression(OptionalType optionalType)87 public CodeBlock parameterizedAbsentValueExpression(OptionalType optionalType) { 88 return CodeBlock.of( 89 "$T.<$T>$L()", className, optionalType.valueType().getTypeName(), absentMethodName); 90 } 91 92 /** Returns an expression for the present {@code value}. */ presentExpression(CodeBlock value)93 public CodeBlock presentExpression(CodeBlock value) { 94 return CodeBlock.of("$T.of($L)", className, value); 95 } 96 97 /** 98 * Returns an expression for the present {@code value}, returning {@code Optional<Object>} no 99 * matter what type the value is. 100 */ presentObjectExpression(CodeBlock value)101 public CodeBlock presentObjectExpression(CodeBlock value) { 102 return CodeBlock.of("$T.<$T>of($L)", className, TypeName.OBJECT, value); 103 } 104 } 105 106 /** The optional type itself. */ typeName()107 abstract TypeName typeName(); 108 109 /** The optional type itself. */ type()110 private XType type() { 111 return type; 112 } 113 114 /** Which {@code Optional} type is used. */ kind()115 public OptionalKind kind() { 116 return OptionalKind.of(type().getTypeElement()); 117 } 118 119 /** The value type. */ valueType()120 public XType valueType() { 121 return type().getTypeArguments().get(0); 122 } 123 124 /** Returns {@code true} if {@code type} is an {@code Optional} type. */ isOptional(XType type)125 private static boolean isOptional(XType type) { 126 return isDeclared(type) && OptionalKind.isOptionalKind(type.getTypeElement()); 127 } 128 129 /** Returns {@code true} if {@code key.type()} is an {@code Optional} type. */ isOptional(Key key)130 public static boolean isOptional(Key key) { 131 return isOptional(key.type().xprocessing()); 132 } 133 134 /** 135 * Returns a {@link OptionalType} for {@code type}. 136 * 137 * @throws IllegalArgumentException if {@code type} is not an {@code Optional} type 138 */ from(XType type)139 public static OptionalType from(XType type) { 140 checkArgument(isOptional(type), "%s must be an Optional", type); 141 OptionalType optionalType = new AutoValue_OptionalType(type.getTypeName()); 142 optionalType.type = type; 143 return optionalType; 144 } 145 146 /** 147 * Returns a {@link OptionalType} for {@code key}'s {@link Key#type() type}. 148 * 149 * @throws IllegalArgumentException if {@code key.type()} is not an {@code Optional} type 150 */ from(Key key)151 public static OptionalType from(Key key) { 152 return from(key.type().xprocessing()); 153 } 154 } 155