1 /* 2 * Copyright (C) 2015 The Dagger Authors. 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 17 package dagger.internal.codegen.binding; 18 19 import static com.google.common.collect.Sets.immutableEnumSet; 20 import static dagger.internal.codegen.base.DiagnosticFormatting.stripCommonTypePrefixes; 21 import static dagger.internal.codegen.base.ElementFormatter.elementToString; 22 import static javax.lang.model.element.ElementKind.PARAMETER; 23 import static javax.lang.model.type.TypeKind.DECLARED; 24 import static javax.lang.model.type.TypeKind.EXECUTABLE; 25 26 import com.google.auto.common.MoreElements; 27 import com.google.auto.common.MoreTypes; 28 import com.google.common.collect.ImmutableList; 29 import com.google.common.collect.ImmutableSet; 30 import dagger.internal.codegen.base.Formatter; 31 import javax.inject.Inject; 32 import javax.lang.model.element.Element; 33 import javax.lang.model.element.TypeElement; 34 import javax.lang.model.type.TypeKind; 35 36 /** 37 * Formats a {@link BindingDeclaration} into a {@link String} suitable for use in error messages. 38 */ 39 public final class BindingDeclarationFormatter extends Formatter<BindingDeclaration> { 40 private static final ImmutableSet<TypeKind> FORMATTABLE_ELEMENT_TYPE_KINDS = 41 immutableEnumSet(EXECUTABLE, DECLARED); 42 43 private final MethodSignatureFormatter methodSignatureFormatter; 44 45 @Inject BindingDeclarationFormatter(MethodSignatureFormatter methodSignatureFormatter)46 BindingDeclarationFormatter(MethodSignatureFormatter methodSignatureFormatter) { 47 this.methodSignatureFormatter = methodSignatureFormatter; 48 } 49 50 /** 51 * Returns {@code true} for declarations that this formatter can format. Specifically bindings 52 * from subcomponent declarations or those with {@linkplain BindingDeclaration#bindingElement() 53 * binding elements} that are methods, constructors, or types. 54 */ canFormat(BindingDeclaration bindingDeclaration)55 public boolean canFormat(BindingDeclaration bindingDeclaration) { 56 if (bindingDeclaration instanceof SubcomponentDeclaration) { 57 return true; 58 } 59 if (bindingDeclaration.bindingElement().isPresent()) { 60 Element bindingElement = bindingDeclaration.bindingElement().get(); 61 return bindingElement.getKind().equals(PARAMETER) 62 || FORMATTABLE_ELEMENT_TYPE_KINDS.contains(bindingElement.asType().getKind()); 63 } 64 // TODO(dpb): validate whether what this is doing is correct 65 return false; 66 } 67 68 @Override format(BindingDeclaration bindingDeclaration)69 public String format(BindingDeclaration bindingDeclaration) { 70 if (bindingDeclaration instanceof SubcomponentDeclaration) { 71 return formatSubcomponentDeclaration((SubcomponentDeclaration) bindingDeclaration); 72 } 73 74 if (bindingDeclaration.bindingElement().isPresent()) { 75 Element bindingElement = bindingDeclaration.bindingElement().get(); 76 if (bindingElement.getKind().equals(PARAMETER)) { 77 return elementToString(bindingElement); 78 } 79 80 switch (bindingElement.asType().getKind()) { 81 case EXECUTABLE: 82 return methodSignatureFormatter.format( 83 MoreElements.asExecutable(bindingElement), 84 bindingDeclaration 85 .contributingModule() 86 .map(module -> MoreTypes.asDeclared(module.asType()))); 87 88 case DECLARED: 89 return stripCommonTypePrefixes(bindingElement.asType().toString()); 90 91 default: 92 throw new IllegalArgumentException( 93 "Formatting unsupported for element: " + bindingElement); 94 } 95 } 96 97 return String.format( 98 "Dagger-generated binding for %s", 99 stripCommonTypePrefixes(bindingDeclaration.key().toString())); 100 } 101 formatSubcomponentDeclaration(SubcomponentDeclaration subcomponentDeclaration)102 private String formatSubcomponentDeclaration(SubcomponentDeclaration subcomponentDeclaration) { 103 ImmutableList<TypeElement> moduleSubcomponents = 104 subcomponentDeclaration.moduleAnnotation().subcomponents(); 105 int index = moduleSubcomponents.indexOf(subcomponentDeclaration.subcomponentType()); 106 StringBuilder annotationValue = new StringBuilder(); 107 if (moduleSubcomponents.size() != 1) { 108 annotationValue.append("{"); 109 } 110 annotationValue.append( 111 formatArgumentInList( 112 index, 113 moduleSubcomponents.size(), 114 subcomponentDeclaration.subcomponentType().getQualifiedName() + ".class")); 115 if (moduleSubcomponents.size() != 1) { 116 annotationValue.append("}"); 117 } 118 119 return String.format( 120 "@%s(subcomponents = %s) for %s", 121 subcomponentDeclaration.moduleAnnotation().annotationName(), 122 annotationValue, 123 subcomponentDeclaration.contributingModule().get()); 124 } 125 } 126