1 /* 2 * Copyright (C) 2014 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 com.google.common.base.Joiner; 20 import com.google.common.collect.ImmutableMap; 21 import dagger.internal.codegen.base.ComponentAnnotation; 22 import java.util.Set; 23 import java.util.function.Function; 24 import java.util.function.UnaryOperator; 25 import javax.lang.model.element.ExecutableElement; 26 import javax.lang.model.element.TypeElement; 27 import javax.lang.model.type.TypeMirror; 28 29 /** The collection of error messages to be reported back to users. */ 30 public final class ErrorMessages { 31 32 private static final UnaryOperator<String> PRODUCTION = 33 s -> 34 s.replace("component", "production component") 35 .replace("Component", "ProductionComponent"); 36 37 private static final UnaryOperator<String> SUBCOMPONENT = 38 s -> s.replace("component", "subcomponent").replace("Component", "Subcomponent"); 39 40 private static final UnaryOperator<String> FACTORY = s -> s.replace("Builder", "Factory"); 41 42 private static final ImmutableMap<ComponentKind, Function<String, String>> 43 COMPONENT_TRANSFORMATIONS = 44 ImmutableMap.of( 45 ComponentKind.COMPONENT, UnaryOperator.identity(), 46 ComponentKind.SUBCOMPONENT, SUBCOMPONENT, 47 ComponentKind.PRODUCTION_COMPONENT, PRODUCTION, 48 ComponentKind.PRODUCTION_SUBCOMPONENT, PRODUCTION.andThen(SUBCOMPONENT)); 49 componentMessagesFor(ComponentKind componentKind)50 public static ComponentMessages componentMessagesFor(ComponentKind componentKind) { 51 return new ComponentMessages(COMPONENT_TRANSFORMATIONS.get(componentKind)); 52 } 53 componentMessagesFor(ComponentAnnotation componentAnnotation)54 public static ComponentMessages componentMessagesFor(ComponentAnnotation componentAnnotation) { 55 return new ComponentMessages( 56 transformation(componentAnnotation.isProduction(), componentAnnotation.isSubcomponent())); 57 } 58 creatorMessagesFor( ComponentCreatorAnnotation creatorAnnotation)59 public static ComponentCreatorMessages creatorMessagesFor( 60 ComponentCreatorAnnotation creatorAnnotation) { 61 Function<String, String> transformation = 62 transformation( 63 creatorAnnotation.isProductionCreatorAnnotation(), 64 creatorAnnotation.isSubcomponentCreatorAnnotation()); 65 switch (creatorAnnotation.creatorKind()) { 66 case BUILDER: 67 return new BuilderMessages(transformation); 68 case FACTORY: 69 return new FactoryMessages(transformation); 70 } 71 throw new AssertionError(creatorAnnotation); 72 } 73 transformation( boolean isProduction, boolean isSubcomponent)74 private static Function<String, String> transformation( 75 boolean isProduction, boolean isSubcomponent) { 76 Function<String, String> transformation = isProduction ? PRODUCTION : UnaryOperator.identity(); 77 return isSubcomponent ? transformation.andThen(SUBCOMPONENT) : transformation; 78 } 79 80 private abstract static class Messages { 81 private final Function<String, String> transformation; 82 Messages(Function<String, String> transformation)83 Messages(Function<String, String> transformation) { 84 this.transformation = transformation; 85 } 86 process(String s)87 protected final String process(String s) { 88 return transformation.apply(s); 89 } 90 } 91 92 /** Errors for components. */ 93 public static final class ComponentMessages extends Messages { ComponentMessages(Function<String, String> transformation)94 ComponentMessages(Function<String, String> transformation) { 95 super(transformation); 96 } 97 moreThanOne()98 public final String moreThanOne() { 99 return process("@Component has more than one @Component.Builder or @Component.Factory: %s"); 100 } 101 } 102 103 /** Errors for component creators. */ 104 public abstract static class ComponentCreatorMessages extends Messages { ComponentCreatorMessages(Function<String, String> transformation)105 ComponentCreatorMessages(Function<String, String> transformation) { 106 super(transformation); 107 } 108 builderMethodRequiresNoArgs()109 public static String builderMethodRequiresNoArgs() { 110 return "Methods returning a @Component.Builder must have no arguments"; 111 } 112 moreThanOneRefToSubcomponent()113 public static String moreThanOneRefToSubcomponent() { 114 return "Only one method can create a given subcomponent. %s is created by: %s"; 115 } 116 invalidConstructor()117 public final String invalidConstructor() { 118 return process("@Component.Builder classes must have exactly one constructor," 119 + " and it must not be private or have any parameters"); 120 } 121 generics()122 public final String generics() { 123 return process("@Component.Builder types must not have any generic types"); 124 } 125 mustBeInComponent()126 public final String mustBeInComponent() { 127 return process("@Component.Builder types must be nested within a @Component"); 128 } 129 mustBeClassOrInterface()130 public final String mustBeClassOrInterface() { 131 return process("@Component.Builder types must be abstract classes or interfaces"); 132 } 133 isPrivate()134 public final String isPrivate() { 135 return process("@Component.Builder types must not be private"); 136 } 137 mustBeStatic()138 public final String mustBeStatic() { 139 return process("@Component.Builder types must be static"); 140 } 141 mustBeAbstract()142 public final String mustBeAbstract() { 143 return process("@Component.Builder types must be abstract"); 144 } 145 missingFactoryMethod()146 public abstract String missingFactoryMethod(); 147 multipleSettersForModuleOrDependencyType()148 public abstract String multipleSettersForModuleOrDependencyType(); 149 extraSetters()150 public abstract String extraSetters(); 151 missingSetters()152 public abstract String missingSetters(); 153 twoFactoryMethods()154 public abstract String twoFactoryMethods(); 155 inheritedTwoFactoryMethods()156 public abstract String inheritedTwoFactoryMethods(); 157 factoryMethodMustReturnComponentType()158 public abstract String factoryMethodMustReturnComponentType(); 159 inheritedFactoryMethodMustReturnComponentType()160 public final String inheritedFactoryMethodMustReturnComponentType() { 161 return factoryMethodMustReturnComponentType() + ". Inherited method: %s"; 162 } 163 factoryMethodMayNotBeAnnotatedWithBindsInstance()164 public abstract String factoryMethodMayNotBeAnnotatedWithBindsInstance(); 165 inheritedFactoryMethodMayNotBeAnnotatedWithBindsInstance()166 public final String inheritedFactoryMethodMayNotBeAnnotatedWithBindsInstance() { 167 return factoryMethodMayNotBeAnnotatedWithBindsInstance() + ". Inherited method: %s"; 168 } 169 setterMethodsMustTakeOneArg()170 public final String setterMethodsMustTakeOneArg() { 171 return process("@Component.Builder methods must not have more than one argument"); 172 } 173 inheritedSetterMethodsMustTakeOneArg()174 public final String inheritedSetterMethodsMustTakeOneArg() { 175 return setterMethodsMustTakeOneArg() + ". Inherited method: %s"; 176 } 177 setterMethodsMustReturnVoidOrBuilder()178 public final String setterMethodsMustReturnVoidOrBuilder() { 179 return process("@Component.Builder setter methods must return void, the builder," 180 + " or a supertype of the builder"); 181 } 182 inheritedSetterMethodsMustReturnVoidOrBuilder()183 public final String inheritedSetterMethodsMustReturnVoidOrBuilder() { 184 return setterMethodsMustReturnVoidOrBuilder() + ". Inherited method: %s"; 185 } 186 methodsMayNotHaveTypeParameters()187 public final String methodsMayNotHaveTypeParameters() { 188 return process("@Component.Builder methods must not have type parameters"); 189 } 190 inheritedMethodsMayNotHaveTypeParameters()191 public final String inheritedMethodsMayNotHaveTypeParameters() { 192 return methodsMayNotHaveTypeParameters() + ". Inherited method: %s"; 193 } 194 nonBindsInstanceParametersMayNotBePrimitives()195 public abstract String nonBindsInstanceParametersMayNotBePrimitives(); 196 inheritedNonBindsInstanceParametersMayNotBePrimitives()197 public final String inheritedNonBindsInstanceParametersMayNotBePrimitives() { 198 return nonBindsInstanceParametersMayNotBePrimitives() + ". Inherited method: %s"; 199 } 200 factoryMethodReturnsSupertypeWithMissingMethods( TypeElement component, TypeElement componentBuilder, TypeMirror returnType, ExecutableElement buildMethod, Set<ExecutableElement> additionalMethods)201 public final String factoryMethodReturnsSupertypeWithMissingMethods( 202 TypeElement component, 203 TypeElement componentBuilder, 204 TypeMirror returnType, 205 ExecutableElement buildMethod, 206 Set<ExecutableElement> additionalMethods) { 207 return String.format( 208 "%1$s.%2$s() returns %3$s, but %4$s declares additional component method(s): %5$s. In " 209 + "order to provide type-safe access to these methods, override %2$s() to return " 210 + "%4$s", 211 componentBuilder.getQualifiedName(), 212 buildMethod.getSimpleName(), 213 returnType, 214 component.getQualifiedName(), 215 Joiner.on(", ").join(additionalMethods)); 216 } 217 bindsInstanceNotAllowedOnBothSetterMethodAndParameter()218 public final String bindsInstanceNotAllowedOnBothSetterMethodAndParameter() { 219 return process("@Component.Builder setter methods may not have @BindsInstance on both the " 220 + "method and its parameter; choose one or the other"); 221 } 222 inheritedBindsInstanceNotAllowedOnBothSetterMethodAndParameter()223 public final String inheritedBindsInstanceNotAllowedOnBothSetterMethodAndParameter() { 224 return bindsInstanceNotAllowedOnBothSetterMethodAndParameter() + ". Inherited method: %s"; 225 } 226 } 227 228 private static final class BuilderMessages extends ComponentCreatorMessages { BuilderMessages(Function<String, String> transformation)229 BuilderMessages(Function<String, String> transformation) { 230 super(transformation); 231 } 232 233 @Override missingFactoryMethod()234 public String missingFactoryMethod() { 235 return process( 236 "@Component.Builder types must have exactly one no-args method that " 237 + " returns the @Component type"); 238 } 239 240 @Override multipleSettersForModuleOrDependencyType()241 public String multipleSettersForModuleOrDependencyType() { 242 return process( 243 "@Component.Builder types must not have more than one setter method per module or " 244 + "dependency, but %s is set by %s"); 245 } 246 247 @Override extraSetters()248 public String extraSetters() { 249 return process( 250 "@Component.Builder has setters for modules or components that aren't required: %s"); 251 } 252 253 @Override missingSetters()254 public String missingSetters() { 255 return process( 256 "@Component.Builder is missing setters for required modules or components: %s"); 257 } 258 259 @Override twoFactoryMethods()260 public String twoFactoryMethods() { 261 return process( 262 "@Component.Builder types must have exactly one zero-arg method, and that" 263 + " method must return the @Component type. Already found: %s"); 264 } 265 266 @Override inheritedTwoFactoryMethods()267 public String inheritedTwoFactoryMethods() { 268 return process( 269 "@Component.Builder types must have exactly one zero-arg method, and that" 270 + " method must return the @Component type. Found %s and %s"); 271 } 272 273 @Override factoryMethodMustReturnComponentType()274 public String factoryMethodMustReturnComponentType() { 275 return process( 276 "@Component.Builder methods that have no arguments must return the @Component type or a " 277 + "supertype of the @Component"); 278 } 279 280 @Override factoryMethodMayNotBeAnnotatedWithBindsInstance()281 public String factoryMethodMayNotBeAnnotatedWithBindsInstance() { 282 return process( 283 "@Component.Builder no-arg build methods may not be annotated with @BindsInstance"); 284 } 285 286 @Override nonBindsInstanceParametersMayNotBePrimitives()287 public String nonBindsInstanceParametersMayNotBePrimitives() { 288 return process( 289 "@Component.Builder methods that are not annotated with @BindsInstance " 290 + "must take either a module or a component dependency, not a primitive"); 291 } 292 } 293 294 private static final class FactoryMessages extends ComponentCreatorMessages { FactoryMessages(Function<String, String> transformation)295 FactoryMessages(Function<String, String> transformation) { 296 super(transformation.andThen(FACTORY)); 297 } 298 299 @Override missingFactoryMethod()300 public String missingFactoryMethod() { 301 return process( 302 "@Component.Factory types must have exactly one method that " 303 + "returns the @Component type"); 304 } 305 306 @Override multipleSettersForModuleOrDependencyType()307 public String multipleSettersForModuleOrDependencyType() { 308 return process( 309 "@Component.Factory methods must not have more than one parameter per module or " 310 + "dependency, but %s is set by %s"); 311 } 312 313 @Override extraSetters()314 public String extraSetters() { 315 return process( 316 "@Component.Factory method has parameters for modules or components that aren't " 317 + "required: %s"); 318 } 319 320 @Override missingSetters()321 public String missingSetters() { 322 return process( 323 "@Component.Factory method is missing parameters for required modules or components: %s"); 324 } 325 326 @Override twoFactoryMethods()327 public String twoFactoryMethods() { 328 return process( 329 "@Component.Factory types must have exactly one abstract method. Already found: %s"); 330 } 331 332 @Override inheritedTwoFactoryMethods()333 public String inheritedTwoFactoryMethods() { 334 return twoFactoryMethods(); 335 } 336 337 @Override factoryMethodMustReturnComponentType()338 public String factoryMethodMustReturnComponentType() { 339 return process( 340 "@Component.Factory abstract methods must return the @Component type or a " 341 + "supertype of the @Component"); 342 } 343 344 @Override factoryMethodMayNotBeAnnotatedWithBindsInstance()345 public String factoryMethodMayNotBeAnnotatedWithBindsInstance() { 346 return process("@Component.Factory method may not be annotated with @BindsInstance"); 347 } 348 349 @Override nonBindsInstanceParametersMayNotBePrimitives()350 public String nonBindsInstanceParametersMayNotBePrimitives() { 351 return process( 352 "@Component.Factory method parameters that are not annotated with @BindsInstance " 353 + "must be either a module or a component dependency, not a primitive"); 354 } 355 } 356 ErrorMessages()357 private ErrorMessages() {} 358 } 359