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