/* * Copyright (C) 2014 The Dagger Authors. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package dagger.internal.codegen.binding; import com.google.common.base.Joiner; import com.google.common.collect.ImmutableMap; import dagger.internal.codegen.base.ComponentAnnotation; import java.util.Set; import java.util.function.Function; import java.util.function.UnaryOperator; import javax.lang.model.element.ExecutableElement; import javax.lang.model.element.TypeElement; import javax.lang.model.type.TypeMirror; /** The collection of error messages to be reported back to users. */ public final class ErrorMessages { private static final UnaryOperator PRODUCTION = s -> s.replace("component", "production component") .replace("Component", "ProductionComponent"); private static final UnaryOperator SUBCOMPONENT = s -> s.replace("component", "subcomponent").replace("Component", "Subcomponent"); private static final UnaryOperator FACTORY = s -> s.replace("Builder", "Factory"); private static final ImmutableMap> COMPONENT_TRANSFORMATIONS = ImmutableMap.of( ComponentKind.COMPONENT, UnaryOperator.identity(), ComponentKind.SUBCOMPONENT, SUBCOMPONENT, ComponentKind.PRODUCTION_COMPONENT, PRODUCTION, ComponentKind.PRODUCTION_SUBCOMPONENT, PRODUCTION.andThen(SUBCOMPONENT)); public static ComponentMessages componentMessagesFor(ComponentKind componentKind) { return new ComponentMessages(COMPONENT_TRANSFORMATIONS.get(componentKind)); } public static ComponentMessages componentMessagesFor(ComponentAnnotation componentAnnotation) { return new ComponentMessages( transformation(componentAnnotation.isProduction(), componentAnnotation.isSubcomponent())); } public static ComponentCreatorMessages creatorMessagesFor( ComponentCreatorAnnotation creatorAnnotation) { Function transformation = transformation( creatorAnnotation.isProductionCreatorAnnotation(), creatorAnnotation.isSubcomponentCreatorAnnotation()); switch (creatorAnnotation.creatorKind()) { case BUILDER: return new BuilderMessages(transformation); case FACTORY: return new FactoryMessages(transformation); } throw new AssertionError(creatorAnnotation); } private static Function transformation( boolean isProduction, boolean isSubcomponent) { Function transformation = isProduction ? PRODUCTION : UnaryOperator.identity(); return isSubcomponent ? transformation.andThen(SUBCOMPONENT) : transformation; } private abstract static class Messages { private final Function transformation; Messages(Function transformation) { this.transformation = transformation; } protected final String process(String s) { return transformation.apply(s); } } /** Errors for components. */ public static final class ComponentMessages extends Messages { ComponentMessages(Function transformation) { super(transformation); } public final String moreThanOne() { return process("@Component has more than one @Component.Builder or @Component.Factory: %s"); } } /** Errors for component creators. */ public abstract static class ComponentCreatorMessages extends Messages { ComponentCreatorMessages(Function transformation) { super(transformation); } public static String builderMethodRequiresNoArgs() { return "Methods returning a @Component.Builder must have no arguments"; } public static String moreThanOneRefToSubcomponent() { return "Only one method can create a given subcomponent. %s is created by: %s"; } public final String invalidConstructor() { return process("@Component.Builder classes must have exactly one constructor," + " and it must not be private or have any parameters"); } public final String generics() { return process("@Component.Builder types must not have any generic types"); } public final String mustBeInComponent() { return process("@Component.Builder types must be nested within a @Component"); } public final String mustBeClassOrInterface() { return process("@Component.Builder types must be abstract classes or interfaces"); } public final String isPrivate() { return process("@Component.Builder types must not be private"); } public final String mustBeStatic() { return process("@Component.Builder types must be static"); } public final String mustBeAbstract() { return process("@Component.Builder types must be abstract"); } public abstract String missingFactoryMethod(); public abstract String multipleSettersForModuleOrDependencyType(); public abstract String extraSetters(); public abstract String missingSetters(); public abstract String twoFactoryMethods(); public abstract String inheritedTwoFactoryMethods(); public abstract String factoryMethodMustReturnComponentType(); public final String inheritedFactoryMethodMustReturnComponentType() { return factoryMethodMustReturnComponentType() + ". Inherited method: %s"; } public abstract String factoryMethodMayNotBeAnnotatedWithBindsInstance(); public final String inheritedFactoryMethodMayNotBeAnnotatedWithBindsInstance() { return factoryMethodMayNotBeAnnotatedWithBindsInstance() + ". Inherited method: %s"; } public final String setterMethodsMustTakeOneArg() { return process("@Component.Builder methods must not have more than one argument"); } public final String inheritedSetterMethodsMustTakeOneArg() { return setterMethodsMustTakeOneArg() + ". Inherited method: %s"; } public final String setterMethodsMustReturnVoidOrBuilder() { return process("@Component.Builder setter methods must return void, the builder," + " or a supertype of the builder"); } public final String inheritedSetterMethodsMustReturnVoidOrBuilder() { return setterMethodsMustReturnVoidOrBuilder() + ". Inherited method: %s"; } public final String methodsMayNotHaveTypeParameters() { return process("@Component.Builder methods must not have type parameters"); } public final String inheritedMethodsMayNotHaveTypeParameters() { return methodsMayNotHaveTypeParameters() + ". Inherited method: %s"; } public abstract String nonBindsInstanceParametersMayNotBePrimitives(); public final String inheritedNonBindsInstanceParametersMayNotBePrimitives() { return nonBindsInstanceParametersMayNotBePrimitives() + ". Inherited method: %s"; } public final String factoryMethodReturnsSupertypeWithMissingMethods( TypeElement component, TypeElement componentBuilder, TypeMirror returnType, ExecutableElement buildMethod, Set additionalMethods) { return String.format( "%1$s.%2$s() returns %3$s, but %4$s declares additional component method(s): %5$s. In " + "order to provide type-safe access to these methods, override %2$s() to return " + "%4$s", componentBuilder.getQualifiedName(), buildMethod.getSimpleName(), returnType, component.getQualifiedName(), Joiner.on(", ").join(additionalMethods)); } public final String bindsInstanceNotAllowedOnBothSetterMethodAndParameter() { return process("@Component.Builder setter methods may not have @BindsInstance on both the " + "method and its parameter; choose one or the other"); } public final String inheritedBindsInstanceNotAllowedOnBothSetterMethodAndParameter() { return bindsInstanceNotAllowedOnBothSetterMethodAndParameter() + ". Inherited method: %s"; } } private static final class BuilderMessages extends ComponentCreatorMessages { BuilderMessages(Function transformation) { super(transformation); } @Override public String missingFactoryMethod() { return process( "@Component.Builder types must have exactly one no-args method that " + " returns the @Component type"); } @Override public String multipleSettersForModuleOrDependencyType() { return process( "@Component.Builder types must not have more than one setter method per module or " + "dependency, but %s is set by %s"); } @Override public String extraSetters() { return process( "@Component.Builder has setters for modules or components that aren't required: %s"); } @Override public String missingSetters() { return process( "@Component.Builder is missing setters for required modules or components: %s"); } @Override public String twoFactoryMethods() { return process( "@Component.Builder types must have exactly one zero-arg method, and that" + " method must return the @Component type. Already found: %s"); } @Override public String inheritedTwoFactoryMethods() { return process( "@Component.Builder types must have exactly one zero-arg method, and that" + " method must return the @Component type. Found %s and %s"); } @Override public String factoryMethodMustReturnComponentType() { return process( "@Component.Builder methods that have no arguments must return the @Component type or a " + "supertype of the @Component"); } @Override public String factoryMethodMayNotBeAnnotatedWithBindsInstance() { return process( "@Component.Builder no-arg build methods may not be annotated with @BindsInstance"); } @Override public String nonBindsInstanceParametersMayNotBePrimitives() { return process( "@Component.Builder methods that are not annotated with @BindsInstance " + "must take either a module or a component dependency, not a primitive"); } } private static final class FactoryMessages extends ComponentCreatorMessages { FactoryMessages(Function transformation) { super(transformation.andThen(FACTORY)); } @Override public String missingFactoryMethod() { return process( "@Component.Factory types must have exactly one method that " + "returns the @Component type"); } @Override public String multipleSettersForModuleOrDependencyType() { return process( "@Component.Factory methods must not have more than one parameter per module or " + "dependency, but %s is set by %s"); } @Override public String extraSetters() { return process( "@Component.Factory method has parameters for modules or components that aren't " + "required: %s"); } @Override public String missingSetters() { return process( "@Component.Factory method is missing parameters for required modules or components: %s"); } @Override public String twoFactoryMethods() { return process( "@Component.Factory types must have exactly one abstract method. Already found: %s"); } @Override public String inheritedTwoFactoryMethods() { return twoFactoryMethods(); } @Override public String factoryMethodMustReturnComponentType() { return process( "@Component.Factory abstract methods must return the @Component type or a " + "supertype of the @Component"); } @Override public String factoryMethodMayNotBeAnnotatedWithBindsInstance() { return process("@Component.Factory method may not be annotated with @BindsInstance"); } @Override public String nonBindsInstanceParametersMayNotBePrimitives() { return process( "@Component.Factory method parameters that are not annotated with @BindsInstance " + "must be either a module or a component dependency, not a primitive"); } } private ErrorMessages() {} }