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.common.base.Optional;
20import com.google.common.base.Predicate;
21import com.google.common.collect.ImmutableList;
22import com.google.common.collect.ImmutableSet;
23import com.google.common.collect.Lists;
24import dagger.MembersInjector;
25import dagger.producers.Producer;
26import java.util.List;
27import java.util.Set;
28import javax.inject.Provider;
29import javax.lang.model.element.Element;
30import javax.lang.model.element.ElementVisitor;
31import javax.lang.model.element.Name;
32import javax.lang.model.element.PackageElement;
33import javax.lang.model.element.TypeElement;
34import javax.lang.model.element.TypeParameterElement;
35import javax.lang.model.type.ArrayType;
36import javax.lang.model.type.DeclaredType;
37import javax.lang.model.type.TypeMirror;
38import javax.lang.model.type.WildcardType;
39import javax.lang.model.util.SimpleElementVisitor6;
40import javax.lang.model.util.SimpleTypeVisitor6;
41import javax.lang.model.util.Types;
42
43import static javax.lang.model.element.Modifier.PUBLIC;
44
45/**
46 * An abstract type for classes representing a Dagger binding.  Particularly, contains the
47 * {@link Element} that generated the binding and the {@link DependencyRequest} instances that are
48 * required to satisfy the binding, but leaves the specifics of the <i>mechanism</i> of the binding
49 * to the subtypes.
50 *
51 * @author Gregory Kick
52 * @since 2.0
53 */
54abstract class Binding {
55
56  /**
57   * The subtype of this binding.
58   */
59  enum Type implements Predicate<Binding> {
60    /** A binding with this type is a {@link ProvisionBinding}. */
61    PROVISION(Provider.class),
62    /** A binding with this type is a {@link MembersInjectionBinding}. */
63    MEMBERS_INJECTION(MembersInjector.class),
64    /** A binding with this type is a {@link ProductionBinding}. */
65    PRODUCTION(Producer.class),
66    ;
67
68    private final Class<?> frameworkClass;
69
70    private Type(Class<?> frameworkClass) {
71      this.frameworkClass = frameworkClass;
72    }
73
74    /**
75     * Returns the framework class associated with bindings of this type.
76     */
77    Class<?> frameworkClass() {
78      return frameworkClass;
79    }
80
81    BindingKey.Kind bindingKeyKind() {
82      switch (this) {
83        case MEMBERS_INJECTION:
84          return BindingKey.Kind.MEMBERS_INJECTION;
85        case PROVISION:
86        case PRODUCTION:
87          return BindingKey.Kind.CONTRIBUTION;
88        default:
89          throw new AssertionError();
90      }
91    }
92
93    @Override
94    public boolean apply(Binding binding) {
95      return this.equals(binding.bindingType());
96    }
97  }
98
99  abstract Binding.Type bindingType();
100
101  /**
102   * Returns the framework class associated with this binding.
103   */
104  Class<?> frameworkClass() {
105    return bindingType().frameworkClass();
106  }
107
108  static Optional<String> bindingPackageFor(Iterable<? extends Binding> bindings) {
109    ImmutableSet.Builder<String> bindingPackagesBuilder = ImmutableSet.builder();
110    for (Binding binding : bindings) {
111      bindingPackagesBuilder.addAll(binding.bindingPackage().asSet());
112    }
113    ImmutableSet<String> bindingPackages = bindingPackagesBuilder.build();
114    switch (bindingPackages.size()) {
115      case 0:
116        return Optional.absent();
117      case 1:
118        return Optional.of(bindingPackages.iterator().next());
119      default:
120        throw new IllegalArgumentException();
121    }
122  }
123
124  /** The {@link Key} that is provided by this binding. */
125  protected abstract Key key();
126
127  BindingKey bindingKey() {
128    return BindingKey.create(bindingType().bindingKeyKind(), key());
129  }
130
131  /** Returns the {@link Element} instance that is responsible for declaring the binding. */
132  abstract Element bindingElement();
133
134  /** The type enclosing the binding {@link #bindingElement()}. */
135  TypeElement bindingTypeElement() {
136    return BINDING_TYPE_ELEMENT.visit(bindingElement());
137  }
138
139  private static final ElementVisitor<TypeElement, Void> BINDING_TYPE_ELEMENT =
140      new SimpleElementVisitor6<TypeElement, Void>() {
141        @Override
142        protected TypeElement defaultAction(Element e, Void p) {
143          return visit(e.getEnclosingElement());
144        }
145
146        @Override
147        public TypeElement visitType(TypeElement e, Void p) {
148          return e;
149        }
150      };
151
152  /**
153   * The explicit set of {@link DependencyRequest dependencies} required to satisfy this binding.
154   */
155  abstract ImmutableSet<DependencyRequest> dependencies();
156
157  /**
158   * The set of {@link DependencyRequest dependencies} required to satisfy this binding. This is a
159   * superset of {@link #dependencies()}.  This returns an unmodifiable set.
160   */
161  abstract Set<DependencyRequest> implicitDependencies();
162
163  /**
164   * Returns the name of the package in which this binding must be managed. E.g.: a binding
165   * may reference non-public types.
166   */
167  abstract Optional<String> bindingPackage();
168
169  protected static Optional<String> findBindingPackage(Key bindingKey) {
170    Set<String> packages = nonPublicPackageUse(bindingKey.type());
171    switch (packages.size()) {
172      case 0:
173        return Optional.absent();
174      case 1:
175        return Optional.of(packages.iterator().next());
176      default:
177        throw new IllegalStateException();
178    }
179  }
180
181  private static Set<String> nonPublicPackageUse(TypeMirror typeMirror) {
182    ImmutableSet.Builder<String> packages = ImmutableSet.builder();
183    typeMirror.accept(new SimpleTypeVisitor6<Void, ImmutableSet.Builder<String>>() {
184      @Override
185      public Void visitArray(ArrayType t, ImmutableSet.Builder<String> p) {
186        return t.getComponentType().accept(this, p);
187      }
188
189      @Override
190      public Void visitDeclared(DeclaredType t, ImmutableSet.Builder<String> p) {
191        for (TypeMirror typeArgument : t.getTypeArguments()) {
192          typeArgument.accept(this, p);
193        }
194        // TODO(gak): address public nested types in non-public types
195        TypeElement typeElement = MoreElements.asType(t.asElement());
196        if (!typeElement.getModifiers().contains(PUBLIC)) {
197          PackageElement elementPackage = MoreElements.getPackage(typeElement);
198          Name qualifiedName = elementPackage.getQualifiedName();
199          p.add(qualifiedName.toString());
200        }
201        // Also make sure enclosing types are visible, otherwise we're fooled by
202        // class Foo { public class Bar }
203        // (Note: we can't use t.getEnclosingType() because it doesn't work!)
204        typeElement.getEnclosingElement().asType().accept(this, p);
205        return null;
206      }
207
208      @Override
209      public Void visitWildcard(WildcardType t, ImmutableSet.Builder<String> p) {
210        if (t.getExtendsBound() != null) {
211          t.getExtendsBound().accept(this, p);
212        }
213        if (t.getSuperBound() != null) {
214          t.getSuperBound().accept(this, p);
215        }
216        return null;
217      }
218    }, packages);
219    return packages.build();
220  }
221
222  /**
223   * Returns true if this is a binding for a key that has a different type parameter list than the
224   * element it's providing.
225   */
226  abstract boolean hasNonDefaultTypeParameters();
227
228  /**
229   * The scope of this binding.
230   */
231  Scope scope() {
232    return Scope.unscoped();
233  }
234
235  // TODO(sameb): Remove the TypeElement parameter and pull it from the TypeMirror.
236  static boolean hasNonDefaultTypeParameters(TypeElement element, TypeMirror type, Types types) {
237    // If the element has no type parameters, nothing can be wrong.
238    if (element.getTypeParameters().isEmpty()) {
239      return false;
240    }
241
242    List<TypeMirror> defaultTypes = Lists.newArrayList();
243    for (TypeParameterElement parameter : element.getTypeParameters()) {
244      defaultTypes.add(parameter.asType());
245    }
246
247    List<TypeMirror> actualTypes =
248        type.accept(
249            new SimpleTypeVisitor6<List<TypeMirror>, Void>() {
250              @Override
251              protected List<TypeMirror> defaultAction(TypeMirror e, Void p) {
252                return ImmutableList.of();
253              }
254
255              @Override
256              public List<TypeMirror> visitDeclared(DeclaredType t, Void p) {
257                return ImmutableList.<TypeMirror>copyOf(t.getTypeArguments());
258              }
259            },
260            null);
261
262    // The actual type parameter size can be different if the user is using a raw type.
263    if (defaultTypes.size() != actualTypes.size()) {
264      return true;
265    }
266
267    for (int i = 0; i < defaultTypes.size(); i++) {
268      if (!types.isSameType(defaultTypes.get(i), actualTypes.get(i))) {
269        return true;
270      }
271    }
272    return false;
273  }
274}
275