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 import static dagger.internal.codegen.xprocessing.XTypes.isTypeOf; 24 25 import androidx.room.compiler.processing.XType; 26 import androidx.room.compiler.processing.XTypeElement; 27 import com.google.auto.value.AutoValue; 28 import com.google.common.collect.ImmutableMap; 29 import com.squareup.javapoet.ClassName; 30 import com.squareup.javapoet.CodeBlock; 31 import com.squareup.javapoet.ParameterizedTypeName; 32 import com.squareup.javapoet.TypeName; 33 import dagger.internal.codegen.javapoet.TypeNames; 34 import dagger.internal.codegen.model.Key; 35 36 /** 37 * Information about an {@code Optional} type. 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 the {@link ClassName} of this optional kind. */ className()75 public ClassName className() { 76 return className; 77 } 78 79 /** Returns {@code valueType} wrapped in the correct class. */ of(TypeName valueType)80 public ParameterizedTypeName of(TypeName valueType) { 81 return ParameterizedTypeName.get(className, valueType); 82 } 83 84 /** Returns an expression for the absent/empty value. */ absentValueExpression()85 public CodeBlock absentValueExpression() { 86 return CodeBlock.of("$T.$L()", className, absentMethodName); 87 } 88 89 /** 90 * Returns an expression for the absent/empty value, parameterized with {@link #valueType()}. 91 */ parameterizedAbsentValueExpression(OptionalType optionalType)92 public CodeBlock parameterizedAbsentValueExpression(OptionalType optionalType) { 93 return CodeBlock.of( 94 "$T.<$T>$L()", className, optionalType.valueType().getTypeName(), absentMethodName); 95 } 96 97 /** Returns an expression for the present {@code value}. */ presentExpression(CodeBlock value)98 public CodeBlock presentExpression(CodeBlock value) { 99 return CodeBlock.of("$T.of($L)", className, value); 100 } 101 102 /** 103 * Returns an expression for the present {@code value}, returning {@code Optional<Object>} no 104 * matter what type the value is. 105 */ presentObjectExpression(CodeBlock value)106 public CodeBlock presentObjectExpression(CodeBlock value) { 107 return CodeBlock.of("$T.<$T>of($L)", className, TypeName.OBJECT, value); 108 } 109 } 110 111 /** The optional type itself. */ typeName()112 abstract TypeName typeName(); 113 114 /** The optional type itself. */ type()115 private XType type() { 116 return type; 117 } 118 119 /** Which {@code Optional} type is used. */ kind()120 public OptionalKind kind() { 121 return OptionalKind.of(type().getTypeElement()); 122 } 123 124 /** The value type. */ valueType()125 public XType valueType() { 126 return type().getTypeArguments().get(0); 127 } 128 129 /** Returns {@code true} if {@code type} is an {@code Optional} type. */ isOptional(XType type)130 private static boolean isOptional(XType type) { 131 return isDeclared(type) && OptionalKind.isOptionalKind(type.getTypeElement()); 132 } 133 134 /** Returns {@code true} if {@code key.type()} is an {@code Optional} type. */ isOptional(Key key)135 public static boolean isOptional(Key key) { 136 return isOptional(key.type().xprocessing()); 137 } 138 139 /** 140 * Returns a {@link OptionalType} for {@code type}. 141 * 142 * @throws IllegalArgumentException if {@code type} is not an {@code Optional} type 143 */ from(XType type)144 public static OptionalType from(XType type) { 145 checkArgument(isOptional(type), "%s must be an Optional", type); 146 OptionalType optionalType = new AutoValue_OptionalType(type.getTypeName()); 147 optionalType.type = type; 148 return optionalType; 149 } 150 151 /** 152 * Returns a {@link OptionalType} for {@code key}'s {@link Key#type() type}. 153 * 154 * @throws IllegalArgumentException if {@code key.type()} is not an {@code Optional} type 155 */ from(Key key)156 public static OptionalType from(Key key) { 157 return from(key.type().xprocessing()); 158 } 159 isOptionalProviderType(XType type)160 public static boolean isOptionalProviderType(XType type) { 161 if (OptionalType.isOptional(type)) { 162 OptionalType optionalType = OptionalType.from(type); 163 if (isTypeOf(optionalType.valueType(), TypeNames.PROVIDER)) { 164 return true; 165 } 166 } 167 return false; 168 } 169 } 170