All Downloads are FREE. Search and download functionalities are using the official Maven repository.

com.github.mttkay.memento.MementoProcessor Maven / Gradle / Ivy

package com.github.mttkay.memento;

import com.squareup.javawriter.JavaWriter;

import javax.annotation.processing.AbstractProcessor;
import javax.annotation.processing.RoundEnvironment;
import javax.annotation.processing.SupportedAnnotationTypes;
import javax.annotation.processing.SupportedSourceVersion;
import javax.lang.model.SourceVersion;
import javax.lang.model.element.Element;
import javax.lang.model.element.Modifier;
import javax.lang.model.element.PackageElement;
import javax.lang.model.element.TypeElement;
import javax.tools.Diagnostic;
import javax.tools.JavaFileObject;
import java.io.IOException;
import java.util.EnumSet;
import java.util.Set;

@SupportedAnnotationTypes("com.github.mttkay.memento.Retain")
@SupportedSourceVersion(SourceVersion.RELEASE_6)
public class MementoProcessor extends AbstractProcessor {

    private static final String LIB_PACKAGE = "com.github.mttkay.memento";

    @Override
    public boolean process(Set annotations, RoundEnvironment roundEnvironment) {
        Set elements = roundEnvironment.getElementsAnnotatedWith(Retain.class);
        if (elements.isEmpty()) {
            return true;
        }

        //TODO: verify activity implements onFirstCreate
        verifyFieldsAccessible(elements);

        log("processing " + elements.size() + " fields");

        Element hostActivity = elements.iterator().next().getEnclosingElement();
        TypeElement activityType = findAndroidActivitySuperType((TypeElement) hostActivity);
        if (activityType == null) {
            throw new IllegalStateException("Annotated type does not seem to be an Activity");
        }

        try {
            generateMemento(elements, hostActivity, activityType);
        } catch (IOException e) {
            throw new RuntimeException("Failed writing class file", e);
        }

        return true;
    }

    private void generateMemento(Set elements, Element hostActivity, TypeElement activityType)
            throws IOException {
        PackageElement packageElement = processingEnv.getElementUtils().getPackageOf(hostActivity);
        final String simpleClassName = hostActivity.getSimpleName() + "$Memento";
        final String qualifiedClassName = packageElement.getQualifiedName() + "." + simpleClassName;

        log("writing class " + qualifiedClassName);
        JavaFileObject sourceFile = processingEnv.getFiler().createSourceFile(
                qualifiedClassName, elements.toArray(new Element[elements.size()]));

        JavaWriter writer = new JavaWriter(sourceFile.openWriter());

        emitClassHeader(packageElement, activityType, writer);

        writer.beginType(qualifiedClassName, "class", EnumSet.of(Modifier.PUBLIC, Modifier.FINAL),
                "Fragment", LIB_PACKAGE + ".MementoMethods");

        emitFields(elements, writer);
        emitConstructor(simpleClassName, writer);
        emitReaderMethod(elements, hostActivity, writer);
        emitWriterMethod(elements, hostActivity, writer);

        writer.endType();
        writer.close();
    }

    private void emitClassHeader(PackageElement packageElement, TypeElement activityType, JavaWriter writer) throws IOException {
        writer.emitPackage(packageElement.getQualifiedName().toString());

        if (isFragmentActivity(activityType)) {
            writer.emitImports("android.support.v4.app.Fragment");
        } else {
            writer.emitImports("android.app.Fragment");
        }
        writer.emitImports("android.app.Activity");
        writer.emitEmptyLine();
    }

    private TypeElement findAndroidActivitySuperType(TypeElement activityType) {
        final String typeName = activityType.getQualifiedName().toString();
        if (typeName.equals("java.lang.Object")) {
            return null;
        } else if (isNativeActivity(activityType) || isFragmentActivity(activityType)) {
            return activityType;
        } else {
            final Element superType = processingEnv.getTypeUtils().asElement(
                    activityType.getSuperclass());
            return findAndroidActivitySuperType((TypeElement) superType);
        }
    }

    private boolean isNativeActivity(TypeElement type) {
        final String typeName = type.getQualifiedName().toString();
        return typeName.equals("android.app.Activity");
    }

    private boolean isFragmentActivity(TypeElement type) {
        final String typeName = type.getQualifiedName().toString();
        return typeName.equals("android.support.v4.app.FragmentActivity");
    }

    private void emitWriterMethod(Set elements, Element hostActivity, JavaWriter writer) throws IOException {
        writer.emitEmptyLine();
        writer.emitAnnotation(Override.class);
        writer.beginMethod("void", "restore", EnumSet.of(Modifier.PUBLIC), "Activity", "target");
        final String activityClass = hostActivity.getSimpleName().toString();
        writer.emitStatement(activityClass + " activity = (" + activityClass + ") target");
        for (Element element : elements) {
            writer.emitStatement("activity." + element.getSimpleName() + " = this." + element.getSimpleName());
        }
        writer.endMethod();
    }

    private void emitReaderMethod(Set elements, Element hostActivity, JavaWriter writer) throws IOException {
        writer.emitEmptyLine();
        writer.emitAnnotation(Override.class);
        writer.beginMethod("void", "retain", EnumSet.of(Modifier.PUBLIC), "Activity", "source");
        final String activityClass = hostActivity.getSimpleName().toString();
        writer.emitStatement(activityClass + " activity = (" + activityClass + ") source");
        for (Element element : elements) {
            writer.emitStatement("this." + element.getSimpleName() + " = activity." + element.getSimpleName());
        }
        writer.endMethod();
    }

    private void emitConstructor(String simpleClassName, JavaWriter writer) throws IOException {
        writer.emitEmptyLine();
        writer.beginMethod(null, simpleClassName, EnumSet.of(Modifier.PUBLIC))
                .emitStatement("setRetainInstance(true)")
                .endMethod();
    }

    private void emitFields(Set elements, JavaWriter writer) throws IOException {
        writer.emitEmptyLine();
        for (Element element : elements) {
            writer.emitField(element.asType().toString(), element.getSimpleName().toString());
        }
    }

    private void verifyFieldsAccessible(Set elements) {
        for (Element element : elements) {
            if (element.getModifiers().contains(Modifier.PRIVATE)) {
                throw new IllegalStateException("Annotated fields cannot be private: " +
                        element.getEnclosingElement() + "#" + element + "(" + element.asType() + ")");
            }
        }
    }

    private void log(String message) {
        processingEnv.getMessager().printMessage(Diagnostic.Kind.NOTE, "Memento: " + message);
    }
}




© 2015 - 2025 Weber Informatics LLC | Privacy Policy