Home | History | Annotate | Download | only in codegen
      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  */
     16 package dagger.internal.codegen;
     17 
     18 import com.google.auto.common.MoreElements;
     19 import com.google.auto.common.MoreTypes;
     20 import com.google.common.base.Optional;
     21 import java.util.List;
     22 import javax.lang.model.element.AnnotationMirror;
     23 import javax.lang.model.element.Element;
     24 import javax.lang.model.element.ElementKind;
     25 import javax.lang.model.element.ExecutableElement;
     26 import javax.lang.model.element.TypeElement;
     27 import javax.lang.model.element.VariableElement;
     28 import javax.lang.model.type.ExecutableType;
     29 import javax.lang.model.type.TypeMirror;
     30 import javax.lang.model.util.SimpleElementVisitor6;
     31 import javax.lang.model.util.Types;
     32 
     33 import static com.google.common.base.Preconditions.checkState;
     34 import static com.google.common.collect.Iterables.getOnlyElement;
     35 import static dagger.internal.codegen.ErrorMessages.INDENT;
     36 
     37 /**
     38  * Formats a {@link DependencyRequest} into a {@link String} suitable for an error message listing
     39  * a chain of dependencies.
     40  *
     41  * @author Christian Gruber
     42  * @since 2.0
     43  */
     44 final class DependencyRequestFormatter extends Formatter<DependencyRequest> {
     45   private final Types types;
     46 
     47   DependencyRequestFormatter(Types types) {
     48     this.types = types;
     49   }
     50 
     51   // TODO(cgruber): Sweep this class for TypeMirror.toString() usage and do some preventive format.
     52   // TODO(cgruber): consider returning a small structure containing strings to be indented later.
     53   @Override public String format(final DependencyRequest request) {
     54     Element requestElement = request.requestElement();
     55     Optional<AnnotationMirror> qualifier = InjectionAnnotations.getQualifier(requestElement);
     56     return requestElement.accept(new SimpleElementVisitor6<String, Optional<AnnotationMirror>>(){
     57 
     58       /* Handle component methods */
     59       @Override public String visitExecutable(
     60           ExecutableElement method, Optional<AnnotationMirror> qualifier) {
     61         StringBuilder builder = new StringBuilder(INDENT);
     62         if (method.getParameters().isEmpty()) {
     63           // some.package.name.MyComponent.myMethod()
     64           //     [component method with return type: @other.package.Qualifier some.package.name.Foo]
     65           appendEnclosingTypeAndMemberName(method, builder).append("()\n")
     66               .append(INDENT).append(INDENT).append("[component method with return type: ");
     67           if (qualifier.isPresent()) {
     68             // TODO(cgruber) use chenying's annotation mirror stringifier
     69             builder.append(qualifier.get()).append(' ');
     70           }
     71           builder.append(method.getReturnType()).append(']');
     72         } else {
     73           // some.package.name.MyComponent.myMethod(some.package.name.Foo foo)
     74           //     [component injection method for type: some.package.name.Foo]
     75           VariableElement componentMethodParameter = getOnlyElement(method.getParameters());
     76           appendEnclosingTypeAndMemberName(method, builder).append("(");
     77           appendParameter(componentMethodParameter, componentMethodParameter.asType(), builder);
     78           builder.append(")\n");
     79           builder.append(INDENT).append(INDENT).append("[component injection method for type: ")
     80               .append(componentMethodParameter.asType())
     81               .append(']');
     82         }
     83         return builder.toString();
     84       }
     85 
     86       /* Handle injected fields or method/constructor parameter injection. */
     87       @Override public String visitVariable(
     88           VariableElement variable, Optional<AnnotationMirror> qualifier) {
     89         StringBuilder builder = new StringBuilder(INDENT);
     90         TypeMirror resolvedVariableType =
     91             MoreTypes.asMemberOf(types, request.enclosingType(), variable);
     92         if (variable.getKind().equals(ElementKind.PARAMETER)) {
     93           // some.package.name.MyClass.myMethod(some.package.name.Foo arg0, some.package.Bar arg1)
     94           //     [parameter: @other.package.Qualifier some.package.name.Foo arg0]
     95           ExecutableElement methodOrConstructor =
     96               MoreElements.asExecutable(variable.getEnclosingElement());
     97           ExecutableType resolvedMethodOrConstructor = MoreTypes.asExecutable(
     98               types.asMemberOf(request.enclosingType(), methodOrConstructor));
     99           appendEnclosingTypeAndMemberName(methodOrConstructor, builder).append('(');
    100           List<? extends VariableElement> parameters = methodOrConstructor.getParameters();
    101           List<? extends TypeMirror> parameterTypes =
    102               resolvedMethodOrConstructor.getParameterTypes();
    103           checkState(parameters.size() == parameterTypes.size());
    104           for (int i = 0; i < parameters.size(); i++) {
    105             appendParameter(parameters.get(i), parameterTypes.get(i), builder);
    106             if (i != parameters.size() - 1) {
    107               builder.append(", ");
    108             }
    109           }
    110           builder.append(")\n").append(INDENT).append(INDENT).append("[parameter: ");
    111         } else {
    112           // some.package.name.MyClass.myField
    113           //     [injected field of type: @other.package.Qualifier some.package.name.Foo myField]
    114           appendEnclosingTypeAndMemberName(variable, builder).append("\n")
    115               .append(INDENT).append(INDENT).append("[injected field of type: ");
    116         }
    117         if (qualifier.isPresent()) {
    118           // TODO(cgruber) use chenying's annotation mirror stringifier
    119           builder.append(qualifier.get()).append(' ');
    120         }
    121         builder.append(resolvedVariableType)
    122             .append(' ')
    123             .append(variable.getSimpleName())
    124             .append(']');
    125         return builder.toString();
    126       }
    127 
    128       @Override
    129       public String visitType(TypeElement e, Optional<AnnotationMirror> p) {
    130         return ""; // types by themselves provide no useful information.
    131       }
    132 
    133       @Override protected String defaultAction(Element element, Optional<AnnotationMirror> ignore) {
    134         throw new IllegalStateException(
    135             "Invalid request " + element.getKind() +  " element " + element);
    136       }
    137     }, qualifier);
    138   }
    139 
    140   private StringBuilder appendParameter(VariableElement parameter, TypeMirror type,
    141       StringBuilder builder) {
    142     return builder.append(type).append(' ').append(parameter.getSimpleName());
    143   }
    144 
    145   private StringBuilder appendEnclosingTypeAndMemberName(Element member, StringBuilder builder) {
    146     TypeElement type = MoreElements.asType(member.getEnclosingElement());
    147     return builder.append(type.getQualifiedName())
    148         .append('.')
    149         .append(member.getSimpleName());
    150   }
    151 }
    152