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