1/*
2 * Copyright (C) 2014 Google, Inc.
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 */
16package dagger.internal.codegen;
17
18import com.google.auto.common.MoreElements;
19import com.google.auto.common.MoreTypes;
20import com.google.auto.value.AutoValue;
21import com.google.common.base.Function;
22import com.google.common.base.Optional;
23import com.google.common.collect.ComparisonChain;
24import com.google.common.collect.FluentIterable;
25import com.google.common.collect.ImmutableSet;
26import com.google.common.collect.ImmutableSortedSet;
27import com.google.common.collect.LinkedHashMultimap;
28import com.google.common.collect.SetMultimap;
29import java.util.ArrayList;
30import java.util.Comparator;
31import java.util.HashSet;
32import java.util.List;
33import java.util.Set;
34import javax.inject.Inject;
35import javax.lang.model.element.Element;
36import javax.lang.model.element.ElementKind;
37import javax.lang.model.element.ElementVisitor;
38import javax.lang.model.element.ExecutableElement;
39import javax.lang.model.element.TypeElement;
40import javax.lang.model.element.VariableElement;
41import javax.lang.model.type.DeclaredType;
42import javax.lang.model.type.ExecutableType;
43import javax.lang.model.type.TypeMirror;
44import javax.lang.model.util.ElementKindVisitor6;
45import javax.lang.model.util.Elements;
46import javax.lang.model.util.Types;
47
48import static com.google.auto.common.MoreElements.isAnnotationPresent;
49import static com.google.common.base.Preconditions.checkArgument;
50import static com.google.common.base.Preconditions.checkNotNull;
51import static com.google.common.base.Preconditions.checkState;
52import static javax.lang.model.element.Modifier.PRIVATE;
53import static javax.lang.model.element.Modifier.STATIC;
54
55/**
56 * Represents the full members injection of a particular type. This does not pay attention to
57 * injected members on supertypes.
58 *
59 * @author Gregory Kick
60 * @since 2.0
61 */
62@AutoValue
63abstract class MembersInjectionBinding extends Binding {
64  @Override abstract TypeElement bindingElement();
65
66  /** The set of individual sites where {@link Inject} is applied. */
67  abstract ImmutableSortedSet<InjectionSite> injectionSites();
68
69  abstract Optional<DependencyRequest> parentInjectorRequest();
70
71  enum Strategy {
72    NO_OP,
73    INJECT_MEMBERS,
74  }
75
76  Strategy injectionStrategy() {
77    return injectionSites().isEmpty() ? Strategy.NO_OP : Strategy.INJECT_MEMBERS;
78  }
79
80  MembersInjectionBinding withoutParentInjectorRequest() {
81    return new AutoValue_MembersInjectionBinding(
82          key(),
83          dependencies(),
84          implicitDependencies(),
85          bindingPackage(),
86          hasNonDefaultTypeParameters(),
87          bindingElement(),
88          injectionSites(),
89          Optional.<DependencyRequest>absent());
90  }
91
92  @Override
93  protected Binding.Type bindingType() {
94    return Binding.Type.MEMBERS_INJECTION;
95  }
96
97  @AutoValue
98  abstract static class InjectionSite {
99    enum Kind {
100      FIELD,
101      METHOD,
102    }
103
104    abstract Kind kind();
105
106    abstract Element element();
107
108    abstract ImmutableSet<DependencyRequest> dependencies();
109
110    protected int indexAmongSiblingMembers(InjectionSite injectionSite) {
111      return injectionSite
112          .element()
113          .getEnclosingElement()
114          .getEnclosedElements()
115          .indexOf(injectionSite.element());
116    }
117  }
118
119  static final class Factory {
120    private final Elements elements;
121    private final Types types;
122    private final Key.Factory keyFactory;
123    private final DependencyRequest.Factory dependencyRequestFactory;
124
125    Factory(Elements elements, Types types, Key.Factory keyFactory,
126        DependencyRequest.Factory dependencyRequestFactory) {
127      this.elements = checkNotNull(elements);
128      this.types = checkNotNull(types);
129      this.keyFactory = checkNotNull(keyFactory);
130      this.dependencyRequestFactory = checkNotNull(dependencyRequestFactory);
131    }
132
133    private InjectionSite injectionSiteForInjectMethod(
134        ExecutableElement methodElement, DeclaredType containingType) {
135      checkNotNull(methodElement);
136      checkArgument(methodElement.getKind().equals(ElementKind.METHOD));
137      ExecutableType resolved =
138          MoreTypes.asExecutable(types.asMemberOf(containingType, methodElement));
139      return new AutoValue_MembersInjectionBinding_InjectionSite(
140          InjectionSite.Kind.METHOD,
141          methodElement,
142          dependencyRequestFactory.forRequiredResolvedVariables(
143              containingType, methodElement.getParameters(), resolved.getParameterTypes()));
144    }
145
146    private InjectionSite injectionSiteForInjectField(
147        VariableElement fieldElement, DeclaredType containingType) {
148      checkNotNull(fieldElement);
149      checkArgument(fieldElement.getKind().equals(ElementKind.FIELD));
150      checkArgument(isAnnotationPresent(fieldElement, Inject.class));
151      TypeMirror resolved = types.asMemberOf(containingType, fieldElement);
152      return new AutoValue_MembersInjectionBinding_InjectionSite(
153          InjectionSite.Kind.FIELD,
154          fieldElement,
155          ImmutableSet.of(
156              dependencyRequestFactory.forRequiredResolvedVariable(
157                  containingType, fieldElement, resolved)));
158    }
159
160    /** Returns an unresolved version of this binding. */
161    MembersInjectionBinding unresolve(MembersInjectionBinding binding) {
162      checkState(binding.hasNonDefaultTypeParameters());
163      DeclaredType unresolved = MoreTypes.asDeclared(binding.bindingElement().asType());
164      return forInjectedType(unresolved, Optional.<TypeMirror>absent());
165    }
166
167    /** Returns true if the type has some injected members in itself or any of its super classes. */
168    boolean hasInjectedMembers(DeclaredType declaredType) {
169      return !getInjectionSites(declaredType).isEmpty();
170    }
171
172    /**
173     * Returns a MembersInjectionBinding for the given type. If {@code resolvedType} is present,
174     * this will return a resolved binding, with the key & type resolved to the given type (using
175     * {@link Types#asMemberOf(DeclaredType, Element)}).
176     */
177    MembersInjectionBinding forInjectedType(
178        DeclaredType declaredType, Optional<TypeMirror> resolvedType) {
179      // If the class this is injecting has some type arguments, resolve everything.
180      if (!declaredType.getTypeArguments().isEmpty() && resolvedType.isPresent()) {
181        DeclaredType resolved = MoreTypes.asDeclared(resolvedType.get());
182        // Validate that we're resolving from the correct type.
183        checkState(
184            types.isSameType(types.erasure(resolved), types.erasure(declaredType)),
185            "erased expected type: %s, erased actual type: %s",
186            types.erasure(resolved),
187            types.erasure(declaredType));
188        declaredType = resolved;
189      }
190      ImmutableSortedSet<InjectionSite> injectionSites = getInjectionSites(declaredType);
191      ImmutableSet<DependencyRequest> dependencies =
192          FluentIterable.from(injectionSites)
193              .transformAndConcat(
194                  new Function<InjectionSite, Set<DependencyRequest>>() {
195                    @Override
196                    public Set<DependencyRequest> apply(InjectionSite input) {
197                      return input.dependencies();
198                    }
199                  })
200              .toSet();
201
202      Optional<DependencyRequest> parentInjectorRequest =
203          MoreTypes.nonObjectSuperclass(types, elements, declaredType)
204              .transform(
205                  new Function<DeclaredType, DependencyRequest>() {
206                    @Override
207                    public DependencyRequest apply(DeclaredType input) {
208                      return dependencyRequestFactory.forMembersInjectedType(input);
209                    }
210                  });
211
212      Key key = keyFactory.forMembersInjectedType(declaredType);
213      TypeElement typeElement = MoreElements.asType(declaredType.asElement());
214      return new AutoValue_MembersInjectionBinding(
215          key,
216          dependencies,
217          dependencies,
218          findBindingPackage(key),
219          hasNonDefaultTypeParameters(typeElement, key.type(), types),
220          typeElement,
221          injectionSites,
222          parentInjectorRequest);
223    }
224
225    private ImmutableSortedSet<InjectionSite> getInjectionSites(DeclaredType declaredType) {
226      Set<InjectionSite> injectionSites = new HashSet<>();
227      final List<TypeElement> ancestors = new ArrayList<>();
228      SetMultimap<String, ExecutableElement> overriddenMethodMap = LinkedHashMultimap.create();
229      for (Optional<DeclaredType> currentType = Optional.of(declaredType);
230          currentType.isPresent();
231          currentType = MoreTypes.nonObjectSuperclass(types, elements, currentType.get())) {
232        final DeclaredType type = currentType.get();
233        ancestors.add(MoreElements.asType(type.asElement()));
234        for (Element enclosedElement : type.asElement().getEnclosedElements()) {
235          Optional<InjectionSite> maybeInjectionSite =
236              injectionSiteVisitor.visit(enclosedElement, type);
237          if (maybeInjectionSite.isPresent()) {
238            InjectionSite injectionSite = maybeInjectionSite.get();
239            if (shouldBeInjected(injectionSite.element(), overriddenMethodMap)) {
240              injectionSites.add(injectionSite);
241            }
242            if (injectionSite.kind() == InjectionSite.Kind.METHOD) {
243              ExecutableElement injectionSiteMethod =
244                  MoreElements.asExecutable(injectionSite.element());
245              overriddenMethodMap.put(
246                  injectionSiteMethod.getSimpleName().toString(), injectionSiteMethod);
247            }
248          }
249        }
250      }
251      return ImmutableSortedSet.copyOf(
252          new Comparator<InjectionSite>() {
253            @Override
254            public int compare(InjectionSite left, InjectionSite right) {
255              return ComparisonChain.start()
256                  // supertypes before subtypes
257                  .compare(
258                      ancestors.indexOf(right.element().getEnclosingElement()),
259                      ancestors.indexOf(left.element().getEnclosingElement()))
260                  // fields before methods
261                  .compare(left.element().getKind(), right.element().getKind())
262                  // then sort by whichever element comes first in the parent
263                  // this isn't necessary, but makes the processor nice and predictable
264                  .compare(
265                      left.indexAmongSiblingMembers(left), right.indexAmongSiblingMembers(right))
266                  .result();
267            }
268          },
269          injectionSites);
270    }
271
272    private boolean shouldBeInjected(
273        Element injectionSite, SetMultimap<String, ExecutableElement> overriddenMethodMap) {
274      if (!isAnnotationPresent(injectionSite, Inject.class)
275          || injectionSite.getModifiers().contains(PRIVATE)
276          || injectionSite.getModifiers().contains(STATIC)) {
277        return false;
278      }
279
280      if (injectionSite.getKind().isField()) { // Inject all fields (self and ancestors)
281        return true;
282      }
283
284      // For each method with the same name belonging to any descendant class, return false if any
285      // method has already overridden the injectionSite method. To decrease the number of methods
286      // that are checked, we store the already injected methods in a SetMultimap and only
287      // check the methods with the same name.
288      ExecutableElement injectionSiteMethod = MoreElements.asExecutable(injectionSite);
289      TypeElement injectionSiteType = MoreElements.asType(injectionSite.getEnclosingElement());
290      for (ExecutableElement method :
291          overriddenMethodMap.get(injectionSiteMethod.getSimpleName().toString())) {
292        if (elements.overrides(method, injectionSiteMethod, injectionSiteType)) {
293          return false;
294        }
295      }
296      return true;
297    }
298
299    private final ElementVisitor<Optional<InjectionSite>, DeclaredType> injectionSiteVisitor =
300        new ElementKindVisitor6<Optional<InjectionSite>, DeclaredType>(
301            Optional.<InjectionSite>absent()) {
302          @Override
303          public Optional<InjectionSite> visitExecutableAsMethod(
304              ExecutableElement e, DeclaredType type) {
305            return Optional.of(injectionSiteForInjectMethod(e, type));
306          }
307
308          @Override
309          public Optional<InjectionSite> visitVariableAsField(
310              VariableElement e, DeclaredType type) {
311            return (isAnnotationPresent(e, Inject.class)
312                    && !e.getModifiers().contains(PRIVATE)
313                    && !e.getModifiers().contains(STATIC))
314                ? Optional.of(injectionSiteForInjectField(e, type))
315                : Optional.<InjectionSite>absent();
316          }
317        };
318  }
319}
320