dagger.internal.codegen.MultibindingsValidator Maven / Gradle / Ivy
Go to download
Show more of this group Show more artifacts with this name
Show all versions of dagger-compiler Show documentation
Show all versions of dagger-compiler Show documentation
A fast dependency injector for Android and Java.
/*
* Copyright (C) 2015 Google, Inc.
*
* 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;
import com.google.auto.common.MoreTypes;
import com.google.common.base.Optional;
import com.google.common.collect.ImmutableListMultimap;
import com.google.common.collect.ImmutableSet;
import dagger.Module;
import dagger.Multibindings;
import dagger.producers.Produced;
import dagger.producers.Producer;
import dagger.producers.ProducerModule;
import java.util.Collection;
import java.util.Map;
import javax.inject.Provider;
import javax.lang.model.element.AnnotationMirror;
import javax.lang.model.element.ExecutableElement;
import javax.lang.model.element.TypeElement;
import javax.lang.model.type.TypeMirror;
import javax.lang.model.util.Elements;
import static com.google.auto.common.MoreElements.getLocalAndInheritedMethods;
import static com.google.auto.common.MoreElements.isAnnotationPresent;
import static com.google.auto.common.MoreTypes.asExecutable;
import static dagger.internal.codegen.ErrorMessages.DUPLICATE_SIZE_LIMIT;
import static dagger.internal.codegen.ErrorMessages.MultibindingsMessages.METHOD_MUST_RETURN_MAP_OR_SET;
import static dagger.internal.codegen.ErrorMessages.MultibindingsMessages.MUST_BE_INTERFACE;
import static dagger.internal.codegen.ErrorMessages.MultibindingsMessages.MUST_BE_IN_MODULE;
import static dagger.internal.codegen.ErrorMessages.MultibindingsMessages.MUST_NOT_HAVE_TYPE_PARAMETERS;
import static dagger.internal.codegen.ErrorMessages.MultibindingsMessages.TOO_MANY_QUALIFIERS;
import static dagger.internal.codegen.ErrorMessages.MultibindingsMessages.tooManyMethodsForKey;
import static dagger.internal.codegen.InjectionAnnotations.getQualifiers;
import static javax.lang.model.element.ElementKind.INTERFACE;
/**
* A {@linkplain ValidationReport validator} for {@link Multibindings @Multibindings}-annotated
* types.
*/
final class MultibindingsValidator {
private final Elements elements;
private final Key.Factory keyFactory;
private final KeyFormatter keyFormatter;
private final MethodSignatureFormatter methodSignatureFormatter;
private final TypeElement objectElement;
MultibindingsValidator(
Elements elements,
Key.Factory keyFactory,
KeyFormatter keyFormatter,
MethodSignatureFormatter methodSignatureFormatter) {
this.elements = elements;
this.keyFactory = keyFactory;
this.keyFormatter = keyFormatter;
this.methodSignatureFormatter = methodSignatureFormatter;
this.objectElement = elements.getTypeElement(Object.class.getCanonicalName());
}
/**
* Returns a report containing validation errors for a
* {@link Multibindings @Multibindings}-annotated type.
*/
public ValidationReport validate(TypeElement multibindingsType) {
ValidationReport.Builder validation = ValidationReport.about(multibindingsType);
if (!multibindingsType.getKind().equals(INTERFACE)) {
validation.addError(MUST_BE_INTERFACE, multibindingsType);
}
if (!multibindingsType.getTypeParameters().isEmpty()) {
validation.addError(MUST_NOT_HAVE_TYPE_PARAMETERS, multibindingsType);
}
Optional bindingType = bindingType(multibindingsType);
if (!bindingType.isPresent()) {
validation.addError(MUST_BE_IN_MODULE, multibindingsType);
}
ImmutableListMultimap.Builder methodsByKey =
ImmutableListMultimap.builder();
for (ExecutableElement method : getLocalAndInheritedMethods(multibindingsType, elements)) {
// Skip methods in Object.
if (method.getEnclosingElement().equals(objectElement)) {
continue;
}
if (!isPlainMap(method.getReturnType()) && !isPlainSet(method.getReturnType())) {
validation.addError(METHOD_MUST_RETURN_MAP_OR_SET, method);
continue;
}
ImmutableSet extends AnnotationMirror> qualifiers = getQualifiers(method);
if (qualifiers.size() > 1) {
for (AnnotationMirror qualifier : qualifiers) {
validation.addError(TOO_MANY_QUALIFIERS, method, qualifier);
}
continue;
}
if (bindingType.isPresent()) {
methodsByKey.put(
keyFactory.forMultibindingsMethod(
bindingType.get(), asExecutable(method.asType()), method),
method);
}
}
for (Map.Entry> entry :
methodsByKey.build().asMap().entrySet()) {
Collection methods = entry.getValue();
if (methods.size() > 1) {
Key key = entry.getKey();
validation.addError(tooManyMultibindingsMethodsForKey(key, methods), multibindingsType);
}
}
return validation.build();
}
private String tooManyMultibindingsMethodsForKey(Key key, Collection methods) {
StringBuilder builder = new StringBuilder(tooManyMethodsForKey(keyFormatter.format(key)));
builder.append(':');
methodSignatureFormatter.formatIndentedList(builder, methods, 1, DUPLICATE_SIZE_LIMIT);
return builder.toString();
}
private Optional bindingType(TypeElement multibindingsType) {
if (isAnnotationPresent(multibindingsType.getEnclosingElement(), Module.class)) {
return Optional.of(BindingType.PROVISION);
} else if (isAnnotationPresent(multibindingsType.getEnclosingElement(), ProducerModule.class)) {
return Optional.of(BindingType.PRODUCTION);
} else {
return Optional.absent();
}
}
private boolean isPlainMap(TypeMirror returnType) {
if (!MapType.isMap(returnType)) {
return false;
}
MapType mapType = MapType.from(returnType);
return !mapType.isRawType()
&& MoreTypes.isType(mapType.valueType()) // No wildcards.
&& !mapType.valuesAreTypeOf(Provider.class)
&& !mapType.valuesAreTypeOf(Producer.class)
&& !mapType.valuesAreTypeOf(Produced.class);
}
private boolean isPlainSet(TypeMirror returnType) {
if (!SetType.isSet(returnType)) {
return false;
}
SetType setType = SetType.from(returnType);
return !setType.isRawType()
&& MoreTypes.isType(setType.elementType()) // No wildcards.
&& !setType.elementsAreTypeOf(Provider.class)
&& !setType.elementsAreTypeOf(Producer.class)
&& !setType.elementsAreTypeOf(Produced.class);
}
}
© 2015 - 2025 Weber Informatics LLC | Privacy Policy