package autodagger.compiler; import com.google.auto.common.MoreElements; import com.google.auto.common.MoreTypes; import com.google.common.collect.ImmutableSet; import com.squareup.javapoet.AnnotationSpec; import com.squareup.javapoet.ClassName; import com.squareup.javapoet.MethodSpec; import com.squareup.javapoet.ParameterSpec; import com.squareup.javapoet.ParameterizedTypeName; import com.squareup.javapoet.TypeName; import org.apache.commons.lang3.StringUtils; import java.lang.annotation.Annotation; import java.util.ArrayList; import java.util.Collections; import java.util.HashSet; import java.util.List; import java.util.Set; import javax.lang.model.element.Element; import javax.lang.model.element.ElementKind; import javax.lang.model.element.Modifier; 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.AutoComponentClassNameUtil; import processorworkflow.AbstractComposer; import processorworkflow.AbstractProcessing; import processorworkflow.Errors; import processorworkflow.Logger; import processorworkflow.ProcessingBuilder; /** * @author Lukasz Piliszczuk - lukasz.pili@gmail.com */ public class ComponentProcessing extends AbstractProcessing<ComponentSpec, State> { /** * Build all extractors first, then build all builders, because * we want to gather all targets first */ private final Set<ComponentExtractor> extractors; public ComponentProcessing(Elements elements, Types types, Errors errors, State state) { super(elements, types, errors, state); extractors = new HashSet<>(); } @Override public Set<Class<? extends Annotation>> supportedAnnotations() { Set set = ImmutableSet.of(AutoComponent.class); return set; } @Override protected void processElements(Set<? extends Element> annotationElements) { super.processElements(annotationElements); if (errors.hasErrors()) { return; } processExtractors(); } @Override public boolean processElement(Element element, Errors.ElementErrors elementErrors) { if (ElementKind.ANNOTATION_TYPE.equals(element.getKind())) { // @AutoComponent is applied on another annotation, find out the targets of that annotation Set<? extends Element> targetElements = roundEnvironment.getElementsAnnotatedWith(MoreElements.asType(element)); for (Element targetElement : targetElements) { process(targetElement, element); if (errors.hasErrors()) { return false; } } return true; } process(element, element); if (errors.hasErrors()) { return false; } return true; } private void process(Element targetElement, Element element) { ComponentExtractor extractor = new ComponentExtractor(targetElement, element, types, elements, errors); if (errors.hasErrors()) { return; } extractors.add(extractor); } private void processExtractors() { for (ComponentExtractor extractor : extractors) { ComponentSpec spec = new Builder(extractor, errors).build(); if (errors.hasErrors()) { return; } specs.add(spec); } } @Override public AbstractComposer<ComponentSpec> createComposer() { return new ComponentComposer(specs); } private class Builder extends ProcessingBuilder<ComponentExtractor, ComponentSpec> { public Builder(ComponentExtractor extractor, Errors errors) { super(extractor, errors); } @Override protected ComponentSpec build() { ComponentSpec componentSpec = new ComponentSpec(AutoComponentClassNameUtil.getComponentClassName(extractor.getComponentElement())); componentSpec.setTargetTypeName(TypeName.get(extractor.getTargetTypeMirror())); if (extractor.getScopeAnnotationTypeMirror() != null) { componentSpec.setScopeAnnotationSpec(AnnotationSpec.get(extractor.getScopeAnnotationTypeMirror())); } // injectors componentSpec.setInjectorSpecs(ProcessingUtil.getAdditions(extractor.getTargetTypeMirror(), state.getInjectorExtractors())); // exposed componentSpec.setExposeSpecs(ProcessingUtil.getAdditions(extractor.getTargetTypeMirror(), state.getExposeExtractors())); // dependencies componentSpec.setDependenciesTypeNames(getDependencies()); // superinterfaces componentSpec.setSuperinterfacesTypeNames(ProcessingUtil.getTypeNames(extractor.getSuperinterfacesTypeMirrors())); // modules componentSpec.setModulesTypeNames(ProcessingUtil.getTypeNames(extractor.getModulesTypeMirrors())); // subcomponents componentSpec.setSubcomponentsSpecs(getSubcomponents()); return componentSpec; } private List<MethodSpec> getSubcomponents() { if (extractor.getSubcomponentsTypeMirrors().isEmpty()) { return Collections.emptyList(); } List<MethodSpec> methodSpecs = new ArrayList<>(extractor.getSubcomponentsTypeMirrors().size()); for (TypeMirror typeMirror : extractor.getSubcomponentsTypeMirrors()) { Element e = MoreTypes.asElement(typeMirror); TypeName typeName; String name; if (MoreElements.isAnnotationPresent(e, AutoSubcomponent.class)) { ClassName cls = AutoComponentClassNameUtil.getComponentClassName(e); typeName = cls; name = cls.simpleName(); } else { typeName = TypeName.get(typeMirror); name = e.getSimpleName().toString(); } List<TypeMirror> modules = state.getSubcomponentModules(typeMirror); List<ParameterSpec> parameterSpecs; if(modules != null) { parameterSpecs = new ArrayList<>(modules.size()); int count = 0; for (TypeMirror moduleTypeMirror : modules) { parameterSpecs.add(ParameterSpec.builder(TypeName.get(moduleTypeMirror), String.format("module%d", ++count)).build()); } } else { parameterSpecs = new ArrayList<>(0); } methodSpecs.add(MethodSpec.methodBuilder("plus" + name) .addModifiers(Modifier.PUBLIC, Modifier.ABSTRACT) .addParameters(parameterSpecs) .returns(typeName) .build()); } return methodSpecs; } private List<TypeName> getDependencies() { List<TypeName> typeNames = new ArrayList<>(); if (extractor.getDependenciesTypeMirrors() == null) { return typeNames; } mainLoop: for (TypeMirror typeMirror : extractor.getDependenciesTypeMirrors()) { // check if dependency type mirror references an @AutoComponent target // if so, build the TypeName that matches the target component for (ComponentExtractor componentExtractor : extractors) { if (componentExtractor == extractor) { // ignore self continue; } if (ProcessingUtil.areTypesEqual(componentExtractor.getTargetTypeMirror(), typeMirror)) { typeNames.add(AutoComponentClassNameUtil.getComponentClassName(componentExtractor.getComponentElement())); continue mainLoop; } } typeNames.add(TypeName.get(typeMirror)); } return typeNames; } } }