1 /*
2  * Copyright (C) 2013 Google, Inc.
3  * Copyright (C) 2013 Square, Inc.
4  *
5  * Licensed under the Apache License, Version 2.0 (the "License");
6  * you may not use this file except in compliance with the License.
7  * You may obtain a copy of the License at
8  *
9  * http://www.apache.org/licenses/LICENSE-2.0
10  *
11  * Unless required by applicable law or agreed to in writing, software
12  * distributed under the License is distributed on an "AS IS" BASIS,
13  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
14  * See the License for the specific language governing permissions and
15  * limitations under the License.
16  */
17 package dagger.internal.codegen;
18 
19 import com.google.auto.common.MoreTypes;
20 import com.google.common.base.Equivalence;
21 import com.google.common.base.Equivalence.Wrapper;
22 import com.google.common.base.Optional;
23 import com.google.common.collect.ImmutableSet;
24 import dagger.producers.Produced;
25 import java.util.Map;
26 import java.util.Set;
27 import javax.inject.Provider;
28 import javax.lang.model.element.Element;
29 import javax.lang.model.element.ExecutableElement;
30 import javax.lang.model.element.Modifier;
31 import javax.lang.model.element.TypeElement;
32 import javax.lang.model.type.DeclaredType;
33 import javax.lang.model.type.TypeMirror;
34 import javax.lang.model.util.Elements;
35 
36 import static com.google.auto.common.MoreElements.getLocalAndInheritedMethods;
37 import static com.google.auto.common.MoreTypes.asDeclared;
38 import static com.google.common.base.Preconditions.checkState;
39 import static javax.lang.model.element.ElementKind.CONSTRUCTOR;
40 import static javax.lang.model.element.Modifier.ABSTRACT;
41 import static javax.lang.model.element.Modifier.PRIVATE;
42 import static javax.lang.model.element.Modifier.STATIC;
43 
44 /**
45  * Utilities for handling types in annotation processors
46  */
47 final class Util {
48   /**
49    * Returns the {@code V} type for a {@link Map} type like {@code Map<K, Provider<V>>} if the map
50    * includes such a construction
51    */
getProvidedValueTypeOfMap(DeclaredType mapType)52   public static TypeMirror getProvidedValueTypeOfMap(DeclaredType mapType) {
53     checkState(MoreTypes.isTypeOf(Map.class, mapType), "%s is not a Map.", mapType);
54     return asDeclared(mapType.getTypeArguments().get(1)).getTypeArguments().get(0);
55   }
56 
57   // TODO(cgruber): Consider an object that holds and exposes the various parts of a Map type.
58   /**
59    * returns the value type for a {@link Map} type like Map<K, V>}.
60    */
getValueTypeOfMap(DeclaredType mapType)61   public static TypeMirror getValueTypeOfMap(DeclaredType mapType) {
62     checkState(MoreTypes.isTypeOf(Map.class, mapType), "%s is not a Map.", mapType);
63     return mapType.getTypeArguments().get(1);
64   }
65 
66   /**
67    * Returns the key type for a {@link Map} type like Map<K, Provider<V>>}
68    */
getKeyTypeOfMap(DeclaredType mapType)69   public static TypeMirror getKeyTypeOfMap(DeclaredType mapType) {
70     checkState(MoreTypes.isTypeOf(Map.class, mapType), "%s is not a Map.", mapType);
71     return mapType.getTypeArguments().get(0);
72   }
73 
74   /**
75    * Returns true if {@code type} is a {@link Map} whose value type is not a {@link Provider}.
76    */
isMapWithNonProvidedValues(TypeMirror type)77   public static boolean isMapWithNonProvidedValues(TypeMirror type) {
78     return MoreTypes.isType(type)
79         && MoreTypes.isTypeOf(Map.class, type)
80         && !MoreTypes.isTypeOf(Provider.class, asDeclared(type).getTypeArguments().get(1));
81   }
82 
83   /**
84    * Returns true if {@code type} is a {@link Map} whose value type is a {@link Provider}.
85    */
isMapWithProvidedValues(TypeMirror type)86   public static boolean isMapWithProvidedValues(TypeMirror type) {
87     return MoreTypes.isType(type)
88         && MoreTypes.isTypeOf(Map.class, type)
89         && MoreTypes.isTypeOf(Provider.class, asDeclared(type).getTypeArguments().get(1));
90   }
91 
92   /** Returns true if {@code type} is a {@code Set<Produced<T>>}. */
isSetOfProduced(TypeMirror type)93   static boolean isSetOfProduced(TypeMirror type) {
94     return MoreTypes.isType(type)
95         && MoreTypes.isTypeOf(Set.class, type)
96         && MoreTypes.isTypeOf(Produced.class, MoreTypes.asDeclared(type).getTypeArguments().get(0));
97   }
98 
99   /**
100    * Wraps an {@link Optional} of a type in an {@code Optional} of a {@link Wrapper} for that type.
101    */
wrapOptionalInEquivalence( Equivalence<T> equivalence, Optional<T> optional)102   static <T> Optional<Equivalence.Wrapper<T>> wrapOptionalInEquivalence(
103       Equivalence<T> equivalence, Optional<T> optional) {
104     return optional.isPresent()
105         ? Optional.of(equivalence.wrap(optional.get()))
106         : Optional.<Equivalence.Wrapper<T>>absent();
107   }
108 
109   /**
110    * Unwraps an {@link Optional} of a {@link Wrapper} into an {@code Optional} of the underlying
111    * type.
112    */
unwrapOptionalEquivalence( Optional<Equivalence.Wrapper<T>> wrappedOptional)113   static <T> Optional<T> unwrapOptionalEquivalence(
114       Optional<Equivalence.Wrapper<T>> wrappedOptional) {
115     return wrappedOptional.isPresent()
116         ? Optional.of(wrappedOptional.get().get())
117         : Optional.<T>absent();
118   }
119 
requiresEnclosingInstance(TypeElement typeElement)120   private static boolean requiresEnclosingInstance(TypeElement typeElement) {
121     switch (typeElement.getNestingKind()) {
122       case TOP_LEVEL:
123         return false;
124       case MEMBER:
125         return !typeElement.getModifiers().contains(STATIC);
126       case ANONYMOUS:
127       case LOCAL:
128         return true;
129       default:
130         throw new AssertionError("TypeElement cannot have nesting kind: "
131             + typeElement.getNestingKind());
132     }
133   }
134 
135   /**
136    * Returns true if and only if a component can instantiate new instances (typically of a module)
137    * rather than requiring that they be passed.
138    */
componentCanMakeNewInstances(TypeElement typeElement)139   static boolean componentCanMakeNewInstances(TypeElement typeElement) {
140     switch (typeElement.getKind()) {
141       case CLASS:
142         break;
143       case ENUM:
144       case ANNOTATION_TYPE:
145       case INTERFACE:
146         return false;
147       default:
148         throw new AssertionError("TypeElement cannot have kind: " + typeElement.getKind());
149     }
150 
151     if (typeElement.getModifiers().contains(ABSTRACT)) {
152       return false;
153     }
154 
155     if (requiresEnclosingInstance(typeElement)) {
156       return false;
157     }
158 
159     for (Element enclosed : typeElement.getEnclosedElements()) {
160       if (enclosed.getKind().equals(CONSTRUCTOR)
161           && ((ExecutableElement) enclosed).getParameters().isEmpty()
162           && !enclosed.getModifiers().contains(PRIVATE)) {
163         return true;
164       }
165     }
166 
167     // TODO(gak): still need checks for visibility
168 
169     return false;
170   }
171 
getUnimplementedMethods( Elements elements, TypeElement type)172   static ImmutableSet<ExecutableElement> getUnimplementedMethods(
173       Elements elements, TypeElement type) {
174     ImmutableSet.Builder<ExecutableElement> unimplementedMethods = ImmutableSet.builder();
175     Set<ExecutableElement> methods = getLocalAndInheritedMethods(type, elements);
176     for (ExecutableElement method : methods) {
177       if (method.getModifiers().contains(Modifier.ABSTRACT)) {
178         unimplementedMethods.add(method);
179       }
180     }
181     return unimplementedMethods.build();
182   }
183 
Util()184   private Util() {}
185 }
186