Package com.enragedginger.stephenerialization.preprocessing

Source Code of com.enragedginger.stephenerialization.preprocessing.StephenerializableAnnotationProcessor

package com.enragedginger.stephenerialization.preprocessing;

import javax.annotation.processing.*;
import javax.lang.model.SourceVersion;
import javax.lang.model.element.*;
import javax.tools.Diagnostic;
import javax.tools.JavaFileObject;
import java.io.IOException;
import java.util.Set;

import com.enragedginger.stephenerialization.annotations.*;

/**
* Processes {@link Stephenerializable} annotations and builds stephenerialization logic
* at compile time instead of run time.
*
* @author Stephen Hopper
*/
@SupportedAnnotationTypes(value = {"com.enragedginger.stephenerialization.annotations.Stephenerializable"})
public class StephenerializableAnnotationProcessor extends AbstractProcessor {

    @Override
    public SourceVersion getSupportedSourceVersion() {
        return SourceVersion.latestSupported();
    }

    @Override
    public boolean process(Set<? extends TypeElement> annotations, RoundEnvironment roundEnv) {
        Messager messager = processingEnv.getMessager();

        for (TypeElement annotation : annotations) {
            for (Element element : roundEnv.getElementsAnnotatedWith(annotation)) {
                try {
                    processAnnotation(element, messager);
                } catch (Exception e) {
                    throw new RuntimeException("An error occurred while preprocessing Stephenerialization annotations.", e);
                }
            }
        }
        return true;
    }

    private void processAnnotation(Element element, Messager messager) throws IOException, ClassNotFoundException {
        Stephenerializable stephenerializable = element.getAnnotation(Stephenerializable.class);
        String className = element.toString();
        String simpleName = element.getSimpleName().toString();
        String generatedClassName = className + "Stephenerializer";
        String simpleGeneratedClassName = simpleName + "Stephenerializer";
        Filer filer = processingEnv.getFiler();
        JavaFileObject sourceFile = filer.createSourceFile(generatedClassName);

        StephenerializationPreprocessorFieldGenerator generator = new StephenerializationPreprocessorFieldGenerator();
        Set<StephenerializationPreprocessorField> fields = generator.generateFields(element);

        PreprocessingWriter w = new PreprocessingWriter(sourceFile.openWriter());

        writePackageImportsAndClass(w, element, simpleGeneratedClassName);

        w.indent();
        writeWriteMethod(w, className, fields, stephenerializable);
        w.writeLine("");
        writeReadMethod(w, className, fields);
        w.deindent();

        //close class
        w.writeLine("}");
        w.close();
        messager.printMessage(Diagnostic.Kind.NOTE, "Generated stephenerializer: " + generatedClassName);
    }

    /**
     * Writes the opening of the stephenerializer class.
     *
     * @param w                        The class's writer.
     * @param element                  The element which references the class which is being processed.
     * @param simpleGeneratedClassName The simple name of the generated class.
     * @throws IOException If an error occurs.
     */
    private void writePackageImportsAndClass(PreprocessingWriter w, Element element, String simpleGeneratedClassName) throws IOException {
        //write package and class declaration name
        PackageElement p = processingEnv.getElementUtils().getPackageOf(element);
        w.writeLine("package " + p.getQualifiedName().toString() + ";\n");
        w.writeLine("import com.enragedginger.stephenerialization.StephenerializationException;");
        w.writeLine("import java.io.ObjectInputStream;");
        w.writeLine("import java.io.ObjectOutputStream;\n");
        w.writeLine("class " + simpleGeneratedClassName + " {");
    }

    /**
     * Creates the read method on the class.
     *
     * @param w                  The class's writer.
     * @param className          The fullname of the class for which this stephenerializer is being created.
     * @param fields             The fields on the object.
     * @param stephenerializable The Stephenerializable annotation on the class.
     * @throws IOException If an error occurs.
     */
    private void writeWriteMethod(PreprocessingWriter w, String className, Set<StephenerializationPreprocessorField> fields,
                                  Stephenerializable stephenerializable) throws IOException {
        //write stephenerialize method
        w.writeLine("public static void stephenerialize(" + className + " object, ObjectOutputStream stream) {");

        w.indent();
        if (fields != null && !fields.isEmpty()) {
            w.writeLine("try {");
            w.indent();
            w.writeLine("stream.writeInt(" + stephenerializable.version() + ");");
            for (StephenerializationPreprocessorField field : fields) {
                w.writeLine("stream." + field.getObjectOutputStreamMethod() + "(object." + field.getGetterName() + "());");
            }
            w.deindent();
            w.writeLine("} catch (Exception e) {");
            w.indent();
            w.writeLine("throw new StephenerializationException(\"An error occurred during Stephenerialization.\", e);");
            w.deindent();
            w.writeLine("}");
        }
        w.deindent();
        w.writeLine("}");
    }

    /**
     * Creates the write method on the class.
     *
     * @param w         The class's writer.
     * @param className The fullname of the class for which this stephenerializer is being created.
     * @param fields    The fields on the object.
     * @throws IOException If an error occurs.
     */
    private void writeReadMethod(PreprocessingWriter w, String className, Set<StephenerializationPreprocessorField> fields) throws IOException {
        //write read method
        w.writeLine("public static void destephenerialize(" + className + " object, ObjectInputStream stream) {");
        w.indent();
        if (fields != null && !fields.isEmpty()) {
            w.writeLine("try {");
            w.indent();
            w.writeLine("final int version = stream.readInt();");

            Integer previousVersion = null;
            for (StephenerializationPreprocessorField field : fields) {
                if (previousVersion == null) {
                    previousVersion = field.getVersion();
                    w.writeLine("if (version >= " + field.getVersion() + ") {");
                }

                if (previousVersion != field.getVersion()) {
                    w.writeLine("}");
                    w.writeLine("if (version >= " + field.getVersion() + ") {");
                }

                w.indent();
                StringBuffer buffy = new StringBuffer();
                buffy.append("object." + field.getSetterName() + "(");
                if (!field.isPrimitive()) {
                    buffy.append("(" + field.getFieldTypeName() + ") ");
                }
                buffy.append("stream." + field.getObjectInputStreamMethod() + "());");
                w.writeLine(buffy.toString());
                w.deindent();
                previousVersion = field.getVersion();
            }
            w.writeLine("}"); //close last if block
            w.deindent();
            w.writeLine("} catch (Exception e) {");
            w.indent();
            w.writeLine("throw new StephenerializationException(\"An error occurred during Destephenerialization.\", e);");
            w.deindent();
            w.writeLine("}");
        }
        w.deindent();
        w.writeLine("}");
    }
}
TOP

Related Classes of com.enragedginger.stephenerialization.preprocessing.StephenerializableAnnotationProcessor

TOP
Copyright © 2018 www.massapi.com. All rights reserved.
All source code are property of their respective owners. Java is a trademark of Sun Microsystems, Inc and owned by ORACLE Inc. Contact coftware#gmail.com.