blob: 8962ade81bfe5849aa6450bc9d4d43930b97052d [file] [log] [blame]
/*
* 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<String> PRODUCTION =
s ->
s.replace("component", "production component")
.replace("Component", "ProductionComponent");
private static final UnaryOperator<String> SUBCOMPONENT =
s -> s.replace("component", "subcomponent").replace("Component", "Subcomponent");
private static final UnaryOperator<String> FACTORY = s -> s.replace("Builder", "Factory");
private static final ImmutableMap<ComponentKind, Function<String, String>>
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<String, String> 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<String, String> transformation(
boolean isProduction, boolean isSubcomponent) {
Function<String, String> transformation = isProduction ? PRODUCTION : UnaryOperator.identity();
return isSubcomponent ? transformation.andThen(SUBCOMPONENT) : transformation;
}
private abstract static class Messages {
private final Function<String, String> transformation;
Messages(Function<String, String> 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<String, String> 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<String, String> 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<ExecutableElement> 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<String, String> 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<String, String> 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() {}
}