001package ball.annotation.processing;
002/*-
003 * ##########################################################################
004 * Utilities
005 * %%
006 * Copyright (C) 2008 - 2022 Allen D. Ball
007 * %%
008 * Licensed under the Apache License, Version 2.0 (the "License");
009 * you may not use this file except in compliance with the License.
010 * You may obtain a copy of the License at
011 *
012 *      http://www.apache.org/licenses/LICENSE-2.0
013 *
014 * Unless required by applicable law or agreed to in writing, software
015 * distributed under the License is distributed on an "AS IS" BASIS,
016 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
017 * See the License for the specific language governing permissions and
018 * limitations under the License.
019 * ##########################################################################
020 */
021import ball.activation.ThrowableDataSource;
022import ball.lang.reflect.JavaLangReflectMethods;
023import ball.tools.javac.AbstractTaskListener;
024import com.sun.source.util.JavacTask;
025import com.sun.source.util.TaskEvent;
026import java.util.Collections;
027import java.util.LinkedHashSet;
028import java.util.Set;
029import java.util.function.Consumer;
030import java.util.function.Predicate;
031import javax.annotation.processing.Completion;
032import javax.annotation.processing.Filer;
033import javax.annotation.processing.ProcessingEnvironment;
034import javax.annotation.processing.Processor;
035import javax.annotation.processing.RoundEnvironment;
036import javax.annotation.processing.SupportedOptions;
037import javax.lang.model.SourceVersion;
038import javax.lang.model.element.AnnotationMirror;
039import javax.lang.model.element.AnnotationValue;
040import javax.lang.model.element.Element;
041import javax.lang.model.element.ExecutableElement;
042import javax.lang.model.element.TypeElement;
043import javax.tools.Diagnostic;
044import lombok.NoArgsConstructor;
045import lombok.Synchronized;
046import lombok.ToString;
047
048import static javax.tools.Diagnostic.Kind.ERROR;
049import static lombok.AccessLevel.PROTECTED;
050
051/**
052 * Provides abstract base class for {@link Processor} by providing a
053 * {@link #getSupportedSourceVersion()} implementation, methods to report
054 * {@link javax.tools.Diagnostic.Kind#ERROR}s and
055 * {@link javax.tools.Diagnostic.Kind#WARNING}s, and access to
056 * {@link ProcessingEnvironment#getFiler()},
057 * {@link ProcessingEnvironment#getElementUtils()}, and
058 * {@link ProcessingEnvironment#getTypeUtils()}.
059 *
060 * {@bean.info}
061 *
062 * @author {@link.uri mailto:ball@hcf.dev Allen D. Ball}
063 */
064@NoArgsConstructor(access = PROTECTED) @ToString
065public abstract class AbstractProcessor extends JavaxLangModelUtilities implements Processor, JavaLangReflectMethods {
066    private final Set<String> options = new LinkedHashSet<>();
067    private ProcessingEnvironment processingEnv = null;
068    /** See {@link JavacTask#instance(ProcessingEnvironment)}. */
069    protected JavacTask javac = null;
070    /** See {@link ProcessingEnvironment#getFiler()}. */
071    protected Filer filer = null;
072
073    {
074        SupportedOptions annotation = getClass().getAnnotation(SupportedOptions.class);
075
076        if (annotation != null) {
077            Collections.addAll(options, annotation.value());
078        }
079    }
080
081    @Override
082    public Set<String> getSupportedOptions() { return options; }
083
084    @Override
085    public abstract Set<String> getSupportedAnnotationTypes();
086
087    @Override
088    public SourceVersion getSupportedSourceVersion() {
089        SourceVersion[] values = SourceVersion.values();
090
091        return values[values.length - 1];
092    }
093
094    @Synchronized
095    @Override
096    public void init(ProcessingEnvironment processingEnv) {
097        this.processingEnv = processingEnv;
098
099        try {
100            filer = processingEnv.getFiler();
101            elements = processingEnv.getElementUtils();
102            types = processingEnv.getTypeUtils();
103
104            javac = JavacTask.instance(processingEnv);
105
106            if (javac != null) {
107                javac.addTaskListener(new WhenAnnotationProcessingFinished());
108            }
109
110            if (fm == null) {
111                fm = Shims.getJavaFileManager(processingEnv);
112            }
113        } catch (Exception exception) {
114            print(ERROR, exception);
115        }
116    }
117
118    @Override
119    public abstract boolean process(Set<? extends TypeElement> annotations, RoundEnvironment roundEnv);
120
121    @Override
122    public Iterable<? extends Completion> getCompletions(Element element, AnnotationMirror annotation, ExecutableElement member, String text) {
123        return Collections.emptyList();
124    }
125
126    /**
127     * Callback method to signal
128     * {@link com.sun.source.util.TaskEvent.Kind#ANNOTATION_PROCESSING} is
129     * finished.
130     */
131    protected void whenAnnotationProcessingFinished() { }
132
133    /**
134     * Method to print a diagnostic message.
135     *
136     * @param   kind            The {@link javax.tools.Diagnostic.Kind}.
137     * @param   format          The message format {@link String}.
138     * @param   argv            Optional arguments to the message format
139     *                          {@link String}.
140     *
141     * @see javax.annotation.processing.Messager#printMessage(Diagnostic.Kind,CharSequence)
142     */
143    protected void print(Diagnostic.Kind kind, String format, Object... argv) {
144        processingEnv.getMessager()
145            .printMessage(kind, String.format(format, argv));
146    }
147
148    /**
149     * Method to print a diagnostic message.
150     *
151     * @param   kind            The {@link javax.tools.Diagnostic.Kind}.
152     * @param   element         The offending {@link Element}.
153     * @param   format          The message format {@link String}.
154     * @param   argv            Optional arguments to the message format
155     *                          {@link String}.
156     *
157     * @see javax.annotation.processing.Messager#printMessage(Diagnostic.Kind,CharSequence,Element)
158     */
159    protected void print(Diagnostic.Kind kind, Element element, String format, Object... argv) {
160        processingEnv.getMessager()
161            .printMessage(kind, String.format(format, argv), element);
162    }
163
164    /**
165     * Method to print a diagnostic message.
166     *
167     * @param   kind            The {@link javax.tools.Diagnostic.Kind}.
168     * @param   element         The offending {@link Element}.
169     * @param   annotation      The offending {@link AnnotationMirror}.
170     * @param   format          The message format {@link String}.
171     * @param   argv            Optional arguments to the message format
172     *                          {@link String}.
173     *
174     * @see javax.annotation.processing.Messager#printMessage(Diagnostic.Kind,CharSequence,Element,AnnotationMirror)
175     */
176    protected void print(Diagnostic.Kind kind, Element element, AnnotationMirror annotation, String format, Object... argv) {
177        processingEnv.getMessager()
178            .printMessage(kind, String.format(format, argv), element, annotation);
179    }
180
181    /**
182     * Method to print a diagnostic message.
183     *
184     * @param   kind            The {@link javax.tools.Diagnostic.Kind}.
185     * @param   element         The offending {@link Element}.
186     * @param   annotation      The offending {@link AnnotationMirror}.
187     * @param   value           The offending {@link AnnotationValue}.
188     * @param   format          The message format {@link String}.
189     * @param   argv            Optional arguments to the message format
190     *                          {@link String}.
191     *
192     * @see javax.annotation.processing.Messager#printMessage(Diagnostic.Kind,CharSequence,Element,AnnotationMirror,AnnotationValue)
193     */
194    protected void print(Diagnostic.Kind kind, Element element, AnnotationMirror annotation, AnnotationValue value, String format, Object... argv) {
195        processingEnv.getMessager()
196            .printMessage(kind, String.format(format, argv), element, annotation, value);
197    }
198
199    /**
200     * Method to print a {@link Throwable}.
201     *
202     * @param   kind            The {@link javax.tools.Diagnostic.Kind}.
203     * @param   throwable       The {@link Throwable}.
204     */
205    protected void print(Diagnostic.Kind kind, Throwable throwable) {
206        print(kind, new ThrowableDataSource(throwable).toString());
207    }
208
209    /**
210     * Abstract {@link Criterion} base class.
211     */
212    @NoArgsConstructor(access = PROTECTED) @ToString
213    protected abstract class Criterion<T extends Element> implements Predicate<T> {
214    }
215
216    /**
217     * Abstract {@link Check} base class.
218     */
219    @NoArgsConstructor(access = PROTECTED) @ToString
220    protected abstract class Check<T extends Element> implements Consumer<T> {
221    }
222
223    @NoArgsConstructor @ToString
224    private class WhenAnnotationProcessingFinished extends AbstractTaskListener {
225        @Override
226        public void finished(TaskEvent event) {
227            switch (event.getKind()) {
228            case ANNOTATION_PROCESSING:
229                javac.removeTaskListener(this);
230                whenAnnotationProcessingFinished();
231                break;
232
233            default:
234                break;
235            }
236        }
237    }
238}