• 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.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