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