package architect.autostack.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.ParameterSpec; import com.squareup.javapoet.TypeName; import org.apache.commons.lang3.StringUtils; import java.lang.annotation.Annotation; import java.util.Set; import javax.lang.model.element.Element; import javax.lang.model.element.VariableElement; import javax.lang.model.util.Elements; import javax.lang.model.util.Types; import architect.robot.AutoStackable; import architect.robot.FromPath; import autodagger.AutoComponent; import autodagger.compiler.utils.AutoComponentClassNameUtil; import processorworkflow.AbstractComposer; import processorworkflow.AbstractProcessing; import processorworkflow.Errors; import processorworkflow.ProcessingBuilder; /** * @author Lukasz Piliszczuk - lukasz.pili@gmail.com */ public class ScopeProcessing extends AbstractProcessing<ScopeSpec, Void> { public ScopeProcessing(Elements elements, Types types, Errors errors, Void aVoid) { super(elements, types, errors, aVoid); } @Override public Set<Class<? extends Annotation>> supportedAnnotations() { Set set = ImmutableSet.of(AutoStackable.class); return set; } @Override public boolean processElement(Element element, Errors.ElementErrors elementErrors) { ScopeExtractor extractor = new ScopeExtractor(element, types, elements, errors); if (errors.hasErrors()) { return false; } ScopeSpec spec = new ElementBuilder(extractor, errors).build(); if (errors.hasErrors()) { return false; } specs.add(spec); return true; } @Override public AbstractComposer<ScopeSpec> createComposer() { return new ScopeComposer(specs); } private class ElementBuilder extends ProcessingBuilder<ScopeExtractor, ScopeSpec> { public ElementBuilder(ScopeExtractor extractor, Errors errors) { super(extractor, errors); } @Override protected ScopeSpec build() { architect.autostack.compiler.ScopeSpec spec = new ScopeSpec(buildClassName(extractor.getElement())); spec.setParentComponentTypeName(TypeName.get(extractor.getComponentDependency())); TypeName presenterTypeName = TypeName.get(extractor.getElement().asType()); ClassName moduleClassName = ClassName.get(spec.getClassName().packageName(), spec.getClassName().simpleName(), "Module"); AnnotationSpec.Builder builder = AnnotationSpec.get(extractor.getComponentAnnotationTypeMirror()).toBuilder(); builder.addMember("target", "$T.class", presenterTypeName); builder.addMember("modules", "$T.class", moduleClassName); spec.setComponentAnnotationSpec(builder.build()); spec.setDaggerComponentTypeName(ClassName.get(spec.getClassName().packageName(), String.format("Dagger%sComponent", spec.getClassName().simpleName()))); // dagger2 builder dependency method name and type can have 3 diff values // - name and type of the generated scope if dependency is annotated with @AutoScope // - name and type of the target if dependency is annotated with @AutoComponent (valid also for #2, so check #2 condition first) // - name and type of the class if dependency is a manually written component String methodName; TypeName typeName; Element daggerDependencyElement = MoreTypes.asElement(extractor.getComponentDependency()); if (MoreElements.isAnnotationPresent(daggerDependencyElement, AutoStackable.class)) { ClassName daggerDependencyScopeClassName = buildClassName(daggerDependencyElement); ClassName daggerDependencyClassName = AutoComponentClassNameUtil.getComponentClassName(daggerDependencyScopeClassName); methodName = StringUtils.uncapitalize(daggerDependencyClassName.simpleName()); typeName = daggerDependencyClassName; } else if (MoreElements.isAnnotationPresent(daggerDependencyElement, AutoComponent.class)) { methodName = StringUtils.uncapitalize(daggerDependencyElement.getSimpleName().toString()) + "Component"; typeName = AutoComponentClassNameUtil.getComponentClassName(daggerDependencyElement); } else { methodName = StringUtils.uncapitalize(daggerDependencyElement.getSimpleName().toString()); typeName = TypeName.get(extractor.getComponentDependency()); } spec.setDaggerComponentBuilderDependencyTypeName(typeName); spec.setDaggerComponentBuilderDependencyMethodName(methodName); if (extractor.getScopeAnnotationTypeMirror() != null) { spec.setScopeAnnotationSpec(AnnotationSpec.get(extractor.getScopeAnnotationTypeMirror())); } if (extractor.getPathViewTypeMirror() != null) { spec.setPathViewTypeName(TypeName.get(extractor.getPathViewTypeMirror())); } if (extractor.getPathLayout() != 0) { spec.setPathLayout(extractor.getPathLayout()); } ModuleSpec moduleSpec = new ModuleSpec(moduleClassName); moduleSpec.setPresenterTypeName(presenterTypeName); moduleSpec.setScopeAnnotationSpec(spec.getScopeAnnotationSpec()); for (VariableElement e : extractor.getConstructorsParamtersElements()) { ParameterSpec parameterSpec = ParameterSpec.builder(TypeName.get(e.asType()), e.getSimpleName().toString()).build(); moduleSpec.getPresenterArgs().add(parameterSpec); // not supported for now: // || extractor.getFromPathFieldsElements().contains(e) if (MoreElements.isAnnotationPresent(e, FromPath.class)) { moduleSpec.getInternalParameters().add(parameterSpec); } else { moduleSpec.getProvideParameters().add(parameterSpec); } } spec.setModuleSpec(moduleSpec); return spec; } private ClassName buildClassName(Element element) { String name = element.getSimpleName().toString(); // try to remove Presenter at the end of the name String newName = removeEndingName(name, "Presenter"); if (newName == null) { errors.addInvalid("Class name " + newName); } String pkg = MoreElements.getPackage(element).getQualifiedName().toString(); if (StringUtils.isBlank(pkg)) { errors.addInvalid("Package name " + pkg); } pkg = pkg + ".stackable"; return ClassName.get(pkg, newName + "Stackable"); } private String removeEndingName(String text, String term) { if (StringUtils.isBlank(text)) { return null; } int index = text.lastIndexOf(term); if (index >= 0) { text = text.substring(0, index); if (StringUtils.isBlank(text)) { return null; } } return text; } } }