package autodagger.compiler; import com.google.auto.common.MoreElements; import com.google.auto.common.MoreTypes; import java.util.ArrayList; import java.util.List; import javax.inject.Scope; import javax.lang.model.element.AnnotationMirror; import javax.lang.model.element.AnnotationValue; import javax.lang.model.element.Element; import javax.lang.model.type.TypeMirror; import javax.lang.model.util.Elements; import javax.lang.model.util.Types; import autodagger.AutoComponent; import autodagger.AutoSubcomponent; import autodagger.compiler.utils.AutoComponentExtractorUtil; import dagger.Component; import dagger.Subcomponent; import processorworkflow.AbstractExtractor; import processorworkflow.Errors; import processorworkflow.ExtractorUtils; /** * @author Lukasz Piliszczuk - lukasz.pili@gmail.com */ public class ComponentExtractor extends AbstractExtractor { /** * The component element represented by @AutoComponent * It's either the element itself, or the element of an annotation if the @AutoComponent * is applied on the annotation */ private final Element componentElement; private TypeMirror targetTypeMirror; private List<TypeMirror> dependenciesTypeMirrors; private List<TypeMirror> modulesTypeMirrors; private List<TypeMirror> superinterfacesTypeMirrors; private List<TypeMirror> subcomponentsTypeMirrors; private AnnotationMirror scopeAnnotationTypeMirror; public ComponentExtractor(Element componentElement, Element element, Types types, Elements elements, Errors errors) { super(element, types, elements, errors); this.componentElement = componentElement; extract(); } @Override public void extract() { targetTypeMirror = ExtractorUtils.getValueFromAnnotation(element, AutoComponent.class, AutoComponentExtractorUtil.ANNOTATION_TARGET); if (targetTypeMirror == null) { targetTypeMirror = componentElement.asType(); } dependenciesTypeMirrors = findTypeMirrors(element, AutoComponentExtractorUtil.ANNOTATION_DEPENDENCIES); modulesTypeMirrors = findTypeMirrors(element, AutoComponentExtractorUtil.ANNOTATION_MODULES); superinterfacesTypeMirrors = findTypeMirrors(element, AutoComponentExtractorUtil.ANNOTATION_SUPERINTERFACES); subcomponentsTypeMirrors = findTypeMirrors(element, AutoComponentExtractorUtil.ANNOTATION_SUBCOMPONENTS); ComponentExtractor includesExtractor = null; TypeMirror includesTypeMirror = ExtractorUtils.getValueFromAnnotation(element, AutoComponent.class, AutoComponentExtractorUtil.ANNOTATION_INCLUDES); if (includesTypeMirror != null) { Element includesElement = MoreTypes.asElement(includesTypeMirror); if (!MoreElements.isAnnotationPresent(includesElement, AutoComponent.class)) { errors.getParent().addInvalid(includesElement, "Included element must be annotated with @AutoComponent"); return; } if (element.equals(includesElement)) { errors.addInvalid("Auto component %s cannot include himself", element.getSimpleName()); return; } includesExtractor = new ComponentExtractor(includesElement, includesElement, types, elements, errors.getParent()); if (errors.getParent().hasErrors()) { return; } } if (includesExtractor != null) { dependenciesTypeMirrors.addAll(includesExtractor.getDependenciesTypeMirrors()); modulesTypeMirrors.addAll(includesExtractor.getModulesTypeMirrors()); superinterfacesTypeMirrors.addAll(includesExtractor.getSuperinterfacesTypeMirrors()); subcomponentsTypeMirrors.addAll(includesExtractor.getSubcomponentsTypeMirrors()); } scopeAnnotationTypeMirror = findScope(); } private List<TypeMirror> findTypeMirrors(Element element, String name) { boolean addsTo = name.equals(AutoComponentExtractorUtil.ANNOTATION_SUBCOMPONENTS); List<TypeMirror> typeMirrors = new ArrayList<>(); List<AnnotationValue> values = ExtractorUtils.getValueFromAnnotation(element, AutoComponent.class, name); if (values != null) { for (AnnotationValue value : values) { if (!validateAnnotationValue(value, name)) { continue; } try { TypeMirror tm = (TypeMirror) value.getValue(); if (addsTo) { Element e = MoreTypes.asElement(tm); if (!MoreElements.isAnnotationPresent(e, AutoSubcomponent.class) && !MoreElements.isAnnotationPresent(e, Subcomponent.class)) { errors.addInvalid("@AutoComponent cannot declare a subcomponent that is not annotated with @Subcomponent or @AutoSubcomponent: %s", e.getSimpleName()); continue; } } typeMirrors.add(tm); } catch (Exception e) { errors.addInvalid(e.getMessage()); break; } } } return typeMirrors; } /** * Find annotation that is itself annoted with @Scope * If there is one, it will be later applied on the generated component * Otherwise the component will be unscoped * Throw error if more than one scope annotation found */ private AnnotationMirror findScope() { // first look on the @AutoComponent annotated element AnnotationMirror annotationMirror = findScope(element); if (annotationMirror == null && element != componentElement) { // look also on the real component element, if @AutoComponent is itself on // an another annotation annotationMirror = findScope(componentElement); } return annotationMirror; } private AnnotationMirror findScope(Element element) { List<AnnotationMirror> annotationMirrors = ExtractorUtil.findAnnotatedAnnotation(element, Scope.class); if (annotationMirrors.isEmpty()) { return null; } if (annotationMirrors.size() > 1) { errors.getParent().addInvalid(element, "Cannot have several scope (@Scope)."); return null; } return annotationMirrors.get(0); } private boolean validateAnnotationValue(AnnotationValue value, String member) { if (!(value.getValue() instanceof TypeMirror)) { errors.addInvalid("%s cannot reference generated class. Use the class that applies the @AutoComponent annotation", member); return false; } return true; } public Element getComponentElement() { return componentElement; } public TypeMirror getTargetTypeMirror() { return targetTypeMirror; } public List<TypeMirror> getDependenciesTypeMirrors() { return dependenciesTypeMirrors; } public List<TypeMirror> getModulesTypeMirrors() { return modulesTypeMirrors; } public List<TypeMirror> getSuperinterfacesTypeMirrors() { return superinterfacesTypeMirrors; } public List<TypeMirror> getSubcomponentsTypeMirrors() { return subcomponentsTypeMirrors; } public AnnotationMirror getScopeAnnotationTypeMirror() { return scopeAnnotationTypeMirror; } }