package architect.autopath.compiler.processing; import com.google.auto.common.MoreElements; import com.google.common.collect.ImmutableSet; import com.squareup.javapoet.ClassName; 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.ExecutableElement; import javax.lang.model.element.Modifier; import javax.lang.model.element.VariableElement; import javax.lang.model.util.Elements; import javax.lang.model.util.Types; import architect.autopath.AutoPath; import architect.autopath.compiler.composer.PathComposer; import architect.autopath.compiler.extractor.PathExtractor; import architect.autopath.compiler.spec.ConstructorSpec; import architect.autopath.compiler.spec.PathSpec; import processorworkflow.AbstractComposer; import processorworkflow.AbstractProcessing; import processorworkflow.Errors; import processorworkflow.ProcessingBuilder; /** * @author Lukasz Piliszczuk - lukasz.pili@gmail.com */ public class PathProcessing extends AbstractProcessing<PathSpec, Void> { public PathProcessing(Elements elements, Types types, Errors errors, Void aVoid) { super(elements, types, errors, aVoid); } @Override public Set<Class<? extends Annotation>> supportedAnnotations() { Set set = ImmutableSet.of(AutoPath.class); return set; } @Override public boolean processElement(Element element, Errors.ElementErrors elementErrors) { PathExtractor extractor = new architect.autopath.compiler.extractor.PathExtractor(element, types, elements, errors); if (errors.hasErrors()) { return false; } PathSpec spec = new ElementBuilder(extractor, errors).build(); if (errors.hasErrors()) { return false; } specs.add(spec); return true; } @Override public AbstractComposer<PathSpec> createComposer() { return new PathComposer(specs); } private class ElementBuilder extends ProcessingBuilder<PathExtractor, PathSpec> { public ElementBuilder(PathExtractor extractor, Errors errors) { super(extractor, errors); } @Override protected PathSpec build() { PathSpec spec = new architect.autopath.compiler.spec.PathSpec(buildClassName(), TypeName.get(extractor.getElement().asType())); spec.setViewTypeName(TypeName.get(extractor.getViewTypeMirror())); boolean defaultConstructorNotPublic = false; int nonDefaultConstructors = 0; for (ExecutableElement e : extractor.getConstructorElements()) { if (e.getParameters().isEmpty()) { if (e.getModifiers().contains(Modifier.PUBLIC)) { spec.getConstructors().add(new ConstructorSpec()); } else { defaultConstructorNotPublic = true; } } else { if (e.getModifiers().contains(Modifier.PUBLIC)) { nonDefaultConstructors++; ConstructorSpec constructorSpec = new ConstructorSpec(); for (VariableElement variableElement : e.getParameters()) { architect.autopath.compiler.spec.ParamSpec paramSpec = new architect.autopath.compiler.spec.ParamSpec(variableElement.getSimpleName().toString(), TypeName.get(variableElement.asType())); constructorSpec.getFields().add(paramSpec); if (!spec.getFields().contains(paramSpec)) { spec.getFields().add(paramSpec); } } spec.getConstructors().add(constructorSpec); } } } if (defaultConstructorNotPublic && spec.getConstructors().isEmpty()) { errors.addInvalid("Path must at least have a public default constructor"); } if (nonDefaultConstructors > 1) { errors.addInvalid("Path cannot have more than one non-default constructor"); } return spec; } private ClassName buildClassName() { String name = extractor.getElement().getSimpleName().toString(); // try to remove NavigationScope at the end of the name String newName = removeEndingName(removeEndingName(name, "Scope"), "Navigation"); if (newName == null) { errors.addInvalid("Class name " + newName); } String pkg = MoreElements.getPackage(extractor.getElement()).getQualifiedName().toString(); if (StringUtils.isBlank(pkg)) { errors.addInvalid("Package name " + pkg); } pkg = pkg + ".path"; return ClassName.get(pkg, newName + "Path"); } 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; } } }