• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 /*
2  * Copyright (C) 2015 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 com.google.common.base.Preconditions.checkState;
21 import static dagger.internal.codegen.xprocessing.XTypes.isTypeOf;
22 import static dagger.internal.codegen.xprocessing.XTypes.unwrapType;
23 
24 import androidx.room.compiler.processing.XType;
25 import com.google.auto.value.AutoValue;
26 import com.google.common.collect.ImmutableSet;
27 import com.squareup.javapoet.ClassName;
28 import com.squareup.javapoet.TypeName;
29 import dagger.internal.codegen.javapoet.TypeNames;
30 import dagger.internal.codegen.model.Key;
31 import dagger.internal.codegen.model.RequestKind;
32 import dagger.internal.codegen.xprocessing.XTypes;
33 
34 /** Information about a {@link java.util.Map} type. */
35 @AutoValue
36 public abstract class MapType {
37   // TODO(b/28555349): support PROVIDER_OF_LAZY here too
38   // TODO(b/376124787): We could consolidate this with a similar list in FrameworkTypes
39   // if we had a better way to go from RequestKind to framework ClassName or vice versa
40   /** The valid framework request kinds allowed on a multibinding map value. */
41   private static final ImmutableSet<RequestKind> VALID_FRAMEWORK_REQUEST_KINDS =
42       ImmutableSet.of(RequestKind.PROVIDER, RequestKind.PRODUCER, RequestKind.PRODUCED);
43 
44   private XType type;
45 
46   /** The map type itself. */
typeName()47   abstract TypeName typeName();
48 
49   /** The map type itself. */
type()50   private XType type() {
51     return type;
52   }
53 
54   /** {@code true} if the map type is the raw {@link java.util.Map} type. */
isRawType()55   public boolean isRawType() {
56     return XTypes.isRawParameterizedType(type());
57   }
58 
59   /**
60    * The map key type.
61    *
62    * @throws IllegalStateException if {@link #isRawType()} is true.
63    */
keyType()64   public XType keyType() {
65     checkState(!isRawType());
66     return type().getTypeArguments().get(0);
67   }
68 
69   /**
70    * The map value type.
71    *
72    * @throws IllegalStateException if {@link #isRawType()} is true.
73    */
valueType()74   public XType valueType() {
75     checkState(!isRawType());
76     return type().getTypeArguments().get(1);
77   }
78 
79   /** Returns {@code true} if the raw type of {@link #valueType()} is {@code className}. */
valuesAreTypeOf(ClassName className)80   public boolean valuesAreTypeOf(ClassName className) {
81     return !isRawType() && isTypeOf(valueType(), className);
82   }
83 
84   /** Returns {@code true} if the raw type of {@link #valueType()} is a framework type. */
valuesAreFrameworkType()85   public boolean valuesAreFrameworkType() {
86     return valueRequestKind() != RequestKind.INSTANCE;
87   }
88 
89   /**
90    * Returns the map's {@link #valueType()} without any wrapping framework type, if one exists.
91    *
92    * <p>In particular, this method returns {@code V} for all of the following map types:
93    * {@code Map<K,V>}, {@code Map<K,Provider<V>>}, {@code Map<K,Producer<V>>}, and
94    * {@code Map<K,Produced<V>>}.
95    *
96    * <p>Note that we don't consider {@code Lazy} a framework type for this particular case, so this
97    * method will return {@code Lazy<V>} for {@code Map<K,Lazy<V>>}.
98    *
99    * @throws IllegalStateException if {@link #isRawType()} is true.
100    */
unwrappedFrameworkValueType()101   public XType unwrappedFrameworkValueType() {
102     return valuesAreFrameworkType() ? unwrapType(valueType()) : valueType();
103   }
104 
105   /**
106    * Returns the {@link RequestKind} of the {@link #valueType()}.
107    *
108    * @throws IllegalArgumentException if {@link #isRawType()} is true.
109    */
valueRequestKind()110   public RequestKind valueRequestKind() {
111     checkArgument(!isRawType());
112     RequestKind requestKind = RequestKinds.getRequestKind(valueType());
113     if (VALID_FRAMEWORK_REQUEST_KINDS.contains(requestKind)) {
114       return requestKind;
115     } else if (requestKind == RequestKind.PROVIDER_OF_LAZY) {
116       // This is kind of a weird case. We don't support Map<K, Lazy<V>>, so we also don't support
117       // Map<K, Provider<Lazy<V>>> directly. However, if the user bound that themselves, we don't
118       // want that to get confused as a normal instance request, so return PROVIDER here.
119       return RequestKind.PROVIDER;
120     } else {
121       // Not all RequestKinds are supported, so if there's a map value that matches an unsupported
122       // RequestKind, just treat it like it is a normal instance request.
123       return RequestKind.INSTANCE;
124     }
125   }
126 
127   /** {@code true} if {@code type} is a {@link java.util.Map} type. */
isMap(XType type)128   public static boolean isMap(XType type) {
129     return isTypeOf(type, TypeNames.MAP);
130   }
131 
132   /** {@code true} if {@code key.type()} is a {@link java.util.Map} type. */
isMap(Key key)133   public static boolean isMap(Key key) {
134     return isMap(key.type().xprocessing());
135   }
136 
isMapOfProvider(XType keyType)137   public static boolean isMapOfProvider(XType keyType) {
138     if (MapType.isMap(keyType)) {
139       return MapType.from(keyType).valuesAreTypeOf(TypeNames.PROVIDER);
140     }
141     return false;
142   }
143 
144   /**
145    * Returns a {@link MapType} for {@code type}.
146    *
147    * @throws IllegalArgumentException if {@code type} is not a {@link java.util.Map} type
148    */
from(XType type)149   public static MapType from(XType type) {
150     checkArgument(isMap(type), "%s is not a Map", type);
151     MapType mapType = new AutoValue_MapType(type.getTypeName());
152     mapType.type = type;
153     return mapType;
154   }
155 
156   /**
157    * Returns a {@link MapType} for {@code key}'s {@link Key#type() type}.
158    *
159    * @throws IllegalArgumentException if {@code key.type()} is not a {@link java.util.Map} type
160    */
from(Key key)161   public static MapType from(Key key) {
162     return from(key.type().xprocessing());
163   }
164 }
165