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