package autodagger.compiler;
import com.google.auto.common.MoreElements;
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.AutoSubcomponent;
import autodagger.compiler.utils.AutoComponentExtractorUtil;
import processorworkflow.AbstractExtractor;
import processorworkflow.Errors;
import processorworkflow.ExtractorUtils;
/**
* @author Lukasz Piliszczuk - lukasz.pili@gmail.com
*/
public class SubcomponentExtractor extends AbstractExtractor {
private List<TypeMirror> modulesTypeMirrors;
private List<TypeMirror> superinterfacesTypeMirrors;
private AnnotationMirror scopeAnnotationTypeMirror;
public SubcomponentExtractor(Element element, Types types, Elements elements, Errors errors) {
super(element, types, elements, errors);
extract();
}
@Override
public void extract() {
modulesTypeMirrors = findTypeMirrors(element, AutoComponentExtractorUtil.ANNOTATION_MODULES);
if (!MoreElements.isAnnotationPresent(element, AutoSubcomponent.class)) {
return;
}
superinterfacesTypeMirrors = findTypeMirrors(element, AutoComponentExtractorUtil.ANNOTATION_SUPERINTERFACES);
scopeAnnotationTypeMirror = findScope();
}
private List<TypeMirror> findTypeMirrors(Element element, String name) {
List<TypeMirror> typeMirrors = new ArrayList<>();
List<AnnotationValue> values = ExtractorUtils.getValueFromAnnotation(element, AutoSubcomponent.class, name);
if (values != null) {
for (AnnotationValue value : values) {
if (!validateAnnotationValue(value, name)) {
continue;
}
try {
TypeMirror tm = (TypeMirror) value.getValue();
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() {
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 List<TypeMirror> getModulesTypeMirrors() {
return modulesTypeMirrors;
}
public AnnotationMirror getScopeAnnotationTypeMirror() {
return scopeAnnotationTypeMirror;
}
public List<TypeMirror> getSuperinterfacesTypeMirrors() {
return superinterfacesTypeMirrors;
}
}