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 */
17package dagger.internal.codegen;
18
19import com.google.auto.common.MoreTypes;
20import com.google.common.base.Equivalence;
21import com.google.common.base.Equivalence.Wrapper;
22import com.google.common.base.Optional;
23import com.google.common.collect.ImmutableSet;
24import dagger.producers.Produced;
25import java.util.Map;
26import java.util.Set;
27import javax.inject.Provider;
28import javax.lang.model.element.Element;
29import javax.lang.model.element.ExecutableElement;
30import javax.lang.model.element.Modifier;
31import javax.lang.model.element.TypeElement;
32import javax.lang.model.type.DeclaredType;
33import javax.lang.model.type.TypeMirror;
34import javax.lang.model.util.Elements;
35
36import static com.google.auto.common.MoreElements.getLocalAndInheritedMethods;
37import static com.google.auto.common.MoreTypes.asDeclared;
38import static com.google.common.base.Preconditions.checkState;
39import static javax.lang.model.element.ElementKind.CONSTRUCTOR;
40import static javax.lang.model.element.Modifier.ABSTRACT;
41import static javax.lang.model.element.Modifier.PRIVATE;
42import static javax.lang.model.element.Modifier.STATIC;
43
44/**
45 * Utilities for handling types in annotation processors
46 */
47final 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   */
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   */
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   */
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   */
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   */
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>>}. */
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   */
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   */
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
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   */
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
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
184  private Util() {}
185}
186