Home | History | Annotate | Download | only in codegen
      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    */
     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