package com.tale.prettybundleprocessor; import com.google.auto.service.AutoService; import com.tale.prettybundle.Extra; import com.tale.prettybundle.ExtraBinder; import java.io.IOException; import java.util.Hashtable; import java.util.LinkedHashSet; import java.util.Map; import java.util.Set; import javax.annotation.processing.AbstractProcessor; import javax.annotation.processing.Filer; import javax.annotation.processing.Messager; import javax.annotation.processing.ProcessingEnvironment; import javax.annotation.processing.Processor; import javax.annotation.processing.RoundEnvironment; import javax.lang.model.SourceVersion; import javax.lang.model.element.Element; import javax.lang.model.element.ElementKind; import javax.lang.model.element.Modifier; import javax.lang.model.element.TypeElement; import javax.lang.model.element.VariableElement; import javax.lang.model.util.Elements; import javax.lang.model.util.Types; import javax.tools.Diagnostic; @AutoService(Processor.class) public class PrettyBundleProcessor extends AbstractProcessor { private Types typeUtils; private Elements elementUtils; private Filer filer; private Messager messager; private ExtraUtilityClassBuilder extraUtilityClassBuilder = new ExtraUtilityClassBuilder(); private Map<String, ExtraClassesGrouped> extraGroupedClassesMap = new Hashtable<String, ExtraClassesGrouped>(); @Override public synchronized void init(ProcessingEnvironment processingEnv) { super.init(processingEnv); typeUtils = processingEnv.getTypeUtils(); elementUtils = processingEnv.getElementUtils(); filer = processingEnv.getFiler(); messager = processingEnv.getMessager(); } @Override public Set<String> getSupportedAnnotationTypes() { Set<String> result = new LinkedHashSet<String>(); result.add(Extra.class.getCanonicalName()); return result; } @Override public SourceVersion getSupportedSourceVersion() { return SourceVersion.latestSupported(); } @Override public boolean process(Set<? extends TypeElement> annotations, RoundEnvironment roundEnv) { for (Element annotatedElement : roundEnv.getElementsAnnotatedWith(Extra.class)) { if (annotatedElement.getKind() != ElementKind.FIELD) { error(annotatedElement, "only fields can be annotated with @%s", Extra.class.getSimpleName()); return true; } final VariableElement annotatedVariableElement = (VariableElement) annotatedElement; final ExtraAnnotatedClass extraAnnotatedClass = new ExtraAnnotatedClass(annotatedVariableElement, elementUtils, typeUtils); if (!isValidClass(extraAnnotatedClass)) { return true; // Error message printed, exit processing } final String activityQualifiedClassName = extraAnnotatedClass.getQualifiedClassName(); ExtraClassesGrouped extraClassesGrouped = extraGroupedClassesMap.get(activityQualifiedClassName); if (extraClassesGrouped == null) { extraClassesGrouped = new ExtraClassesGrouped(activityQualifiedClassName); extraGroupedClassesMap.put(activityQualifiedClassName, extraClassesGrouped); } extraClassesGrouped.add(extraAnnotatedClass); if (!extraUtilityClassBuilder.contains(extraClassesGrouped)) { extraUtilityClassBuilder.add(extraClassesGrouped); } } try { // Generate Activities util class. extraUtilityClassBuilder.generateCode(elementUtils, typeUtils, filer); // Generate Activity$$Injector classes. for (ExtraClassesGrouped extraClassesGrouped : extraGroupedClassesMap.values()) { try { new ExtraInjectorClassBuilder(extraClassesGrouped).generateCode(elementUtils, filer); } catch (IllegalAccessException e) { error(null, e.getMessage()); } } extraUtilityClassBuilder.clear(); extraGroupedClassesMap.clear(); } catch (IOException e) { error(null, e.getMessage()); } return true; } private String getPackageOfActivity(String activityQualifiedClassName) { final TypeElement typeElement = elementUtils.getTypeElement(activityQualifiedClassName); return elementUtils.getPackageOf(typeElement).getQualifiedName().toString(); } private boolean isValidClass(ExtraAnnotatedClass extraAnnotatedClass) { // Verify if class is supported or not. if (extraAnnotatedClass.getSupportedType() == SupportedType.NOP) { error(extraAnnotatedClass.getAnnotatedVariableElement(), "Only support Activity, Fragment and Service"); } // Verify modifiers is not private or protected if (extraAnnotatedClass.getAnnotatedVariableElement().getModifiers().contains(Modifier.PRIVATE)) { error(extraAnnotatedClass.getAnnotatedVariableElement(), "The data type must not declared by private modifier"); } else if (extraAnnotatedClass.getAnnotatedVariableElement().getModifiers().contains(Modifier.PROTECTED)) { error(extraAnnotatedClass.getAnnotatedVariableElement(), "The data type must not declared by protected modifier"); } // Check if data type is supported or not. if (!isDataTypeSupported(extraAnnotatedClass)) { error(extraAnnotatedClass.getAnnotatedVariableElement(), "Data type: %s is not supported", extraAnnotatedClass.getDataTypeQualifiedClassName()); return false; } return true; } private boolean isDataTypeSupported(ExtraAnnotatedClass extraAnnotatedClass) { if (extraAnnotatedClass.getExtraBinder() == ExtraBinder.NOP) { return false; } return true; } private void error(Element element, String message, Object... args) { messager.printMessage(Diagnostic.Kind.ERROR, String.format(message, args), element); } }