mbenson.annotationprocessing.ProcessorBase.java Source code

Java tutorial

Introduction

Here is the source code for mbenson.annotationprocessing.ProcessorBase.java

Source

/*
 * Copyright the original author or authors.
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *     http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */
package mbenson.annotationprocessing;

import java.io.PrintWriter;
import java.io.StringWriter;

import javax.annotation.processing.AbstractProcessor;
import javax.lang.model.element.AnnotationMirror;
import javax.lang.model.element.AnnotationValue;
import javax.lang.model.element.Element;
import javax.lang.model.util.Elements;
import javax.lang.model.util.Types;
import javax.tools.Diagnostic;
import javax.tools.Diagnostic.Kind;

import org.apache.commons.lang3.ObjectUtils;
import org.apache.commons.lang3.Validate;

/**
 * Abstract base class to provide further functionality atop
 * {@link AbstractProcessor}.
 */
public abstract class ProcessorBase extends AbstractProcessor {

    private interface MessageWriter {
        void write(Diagnostic.Kind kind, String message, Object... arguments);
    }

    private static ThreadLocal<MessageWriter> MESSAGE_WRITER = new ThreadLocal<MessageWriter>();

    private MessageWriter defaultMessageWriter = new MessageWriter() {

        @Override
        public void write(Kind kind, String message, Object... arguments) {
            processingEnv.getMessager().printMessage(kind, String.format(message, arguments));

        }
    };

    /**
     * Internal process of a single element. Handles message printing based on
     * constructor args.
     * 
     * @param <T>
     *            element type
     */
    public abstract class Process<T extends Element> {
        protected final T element;
        protected final AnnotationMirror annotation;
        protected final AnnotationValue value;
        private final MessageWriter messageWriter;

        /**
         * Create a new Process instance.
         * 
         * @param element
         */
        protected Process(final T element) {
            super();
            this.element = Validate.notNull(element);
            this.annotation = null;
            this.value = null;
            this.messageWriter = new MessageWriter() {

                @Override
                public void write(Kind kind, String message, Object... arguments) {
                    processingEnv.getMessager().printMessage(kind, String.format(message, arguments), element);

                }
            };
        }

        /**
         * Create a new Process instance.
         * 
         * @param element
         * @param annotation
         */
        protected Process(final T element, final AnnotationMirror annotation) {
            super();
            this.element = Validate.notNull(element);
            this.annotation = Validate.notNull(annotation);
            this.value = null;
            this.messageWriter = new MessageWriter() {

                @Override
                public void write(Kind kind, String message, Object... arguments) {
                    processingEnv.getMessager().printMessage(kind, String.format(message, arguments), element,
                            annotation);

                }
            };
        }

        /**
         * Create a new Process instance.
         * 
         * @param element
         * @param annotation
         * @param value
         */
        protected Process(final T element, final AnnotationMirror annotation, final AnnotationValue value) {
            super();
            this.element = Validate.notNull(element);
            this.annotation = Validate.notNull(annotation);
            this.value = Validate.notNull(value);
            this.messageWriter = new MessageWriter() {

                @Override
                public void write(Kind kind, String message, Object... arguments) {
                    processingEnv.getMessager().printMessage(kind, String.format(message, arguments), element,
                            annotation, value);
                }
            };
        }

        /**
         * Process.
         */
        public final void process() {
            MessageWriter orig = MESSAGE_WRITER.get();
            MESSAGE_WRITER.set(messageWriter);
            try {
                processImpl();
            } catch (Throwable t) {
                error(t, "Processing error:");
            } finally {
                MESSAGE_WRITER.set(orig);
            }
        }

        /**
         * Process implementation.
         */
        protected abstract void processImpl() throws Throwable;

    }

    // TODO subclass again CodeModelProcessorBase
    // /**
    // * Process Snapin annotations found.
    // *
    // * @param annotations
    // * to process
    // * @param roundEnv
    // * context
    // * @return whether the annotations were claimed
    // */
    // @Override
    // public boolean process(Set<? extends TypeElement> annotations,
    // RoundEnvironment roundEnv) {
    // if (annotations != null) {
    // for (Element element : roundEnv.getElementsAnnotatedWith(Snapin.class)) {
    // if (element.getKind() != ElementKind.CLASS) {
    // continue;
    // }
    // final TypeElement type = (TypeElement) element;
    // JCodeModel codeModel = new JCodeModel();
    // Worker worker = new Worker(type, codeModel);
    // try {
    // worker.build();
    // } catch (Exception e) {
    // worker.error(e, "Error creating snapin code model");
    // }
    // try {
    // final CodeWriter codeWriter = new CodeWriter() {
    //
    // @Override
    // public OutputStream openBinary(JPackage pkg, String fileName) throws
    // IOException {
    // JavaFileObject sourceFile =
    // processingEnv.getFiler().createSourceFile(
    // StringUtils.join(pkg.name(), ".",
    // StringUtils.removeEnd(fileName, JAVA_FILE_EXTENSION)), type);
    // return sourceFile.openOutputStream();
    // }
    //
    // @Override
    // public void close() throws IOException {
    // }
    // };
    // codeModel.build(new PrologCodeWriter(codeWriter,
    // String.format("generated by %s\n",
    // ProcessorBase.class.getName())));
    // } catch (IOException e) {
    // worker.error(e, "Error generating code");
    // }
    // }
    // }
    // return true;
    // }

    /**
     * Convenience method to access {@link Elements}.
     * 
     * @return Elements
     */
    protected Elements elements() {
        return processingEnv.getElementUtils();
    }

    /**
     * Convenience method to access {@link Types}.
     * 
     * @return Types
     */
    protected Types types() {
        return processingEnv.getTypeUtils();
    }

    /**
     * Validate a condition, printing an error if {@code false}.
     * 
     * @param condition
     * @param message
     * @param arguments
     */
    protected void validate(boolean condition, String message, Object... arguments) {
        if (!condition) {
            printMessage(Kind.ERROR, message, arguments);
        }
    }

    /**
     * Generate an error message.
     * 
     * @param cause
     * @param message
     * @param args
     */
    protected void error(Throwable cause, String message, Object... args) {
        StringWriter msg = new StringWriter();
        PrintWriter w = new PrintWriter(msg);
        w.format(message, args);
        w.println();
        if (cause != null) {
            cause.printStackTrace(w);
        }
        printMessage(Kind.ERROR, msg.toString());
    }

    /**
     * Print a message.
     * 
     * @param kind
     * @param message
     * @param arguments
     */
    protected void printMessage(Kind kind, String message, Object... arguments) {
        ObjectUtils.defaultIfNull(MESSAGE_WRITER.get(), defaultMessageWriter).write(kind, message, arguments);
    }
}