001package ball.annotation.processing;
002/*-
003 * ##########################################################################
004 * Utilities
005 * $Id: AbstractProcessor.java 6125 2020-06-06 15:01:50Z 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 - 2020 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.beans.PropertyMethodEnum;
025import ball.lang.reflect.JavaLangReflectMethods;
026import ball.tools.javac.AbstractTaskListener;
027import com.sun.source.util.JavacTask;
028import com.sun.source.util.TaskEvent;
029import com.sun.source.util.Trees;
030import com.sun.tools.javac.processing.JavacProcessingEnvironment;
031import com.sun.tools.javac.util.Context;
032import java.lang.annotation.Annotation;
033import java.lang.reflect.Method;
034import java.net.URLClassLoader;
035import java.nio.file.Path;
036import java.nio.file.Paths;
037import java.util.Arrays;
038import java.util.Collection;
039import java.util.EnumSet;
040import java.util.List;
041import java.util.Objects;
042import java.util.Optional;
043import java.util.Set;
044import java.util.TreeSet;
045import java.util.function.Consumer;
046import java.util.function.Function;
047import java.util.function.Predicate;
048import java.util.regex.Pattern;
049import java.util.stream.IntStream;
050import java.util.stream.Stream;
051import javax.annotation.processing.Filer;
052import javax.annotation.processing.ProcessingEnvironment;
053import javax.annotation.processing.RoundEnvironment;
054import javax.lang.model.SourceVersion;
055import javax.lang.model.element.AnnotationMirror;
056import javax.lang.model.element.AnnotationValue;
057import javax.lang.model.element.Element;
058import javax.lang.model.element.ExecutableElement;
059import javax.lang.model.element.Modifier;
060import javax.lang.model.element.PackageElement;
061import javax.lang.model.element.TypeElement;
062import javax.lang.model.type.TypeKind;
063import javax.lang.model.type.TypeMirror;
064import javax.lang.model.util.Elements;
065import javax.lang.model.util.Types;
066import javax.tools.Diagnostic;
067import javax.tools.FileObject;
068import javax.tools.JavaFileManager;
069import lombok.NoArgsConstructor;
070import lombok.ToString;
071
072import static java.util.Collections.disjoint;
073import static java.util.stream.Collectors.toList;
074import static javax.lang.model.element.ElementKind.CONSTRUCTOR;
075import static javax.lang.model.element.ElementKind.METHOD;
076import static javax.lang.model.element.Modifier.PRIVATE;
077import static javax.lang.model.element.Modifier.STATIC;
078import static javax.lang.model.util.ElementFilter.constructorsIn;
079import static javax.lang.model.util.ElementFilter.methodsIn;
080import static javax.tools.Diagnostic.Kind.ERROR;
081import static javax.tools.StandardLocation.CLASS_PATH;
082import static lombok.AccessLevel.PROTECTED;
083
084/**
085 * Extends {@link javax.annotation.processing.AbstractProcessor} by
086 * providing a {@link #getSupportedSourceVersion()} implementation, methods
087 * to report {@link javax.tools.Diagnostic.Kind#ERROR}s and
088 * {@link javax.tools.Diagnostic.Kind#WARNING}s, and access to
089 * {@link ProcessingEnvironment#getFiler()},
090 * {@link ProcessingEnvironment#getElementUtils()}, and
091 * {@link ProcessingEnvironment#getTypeUtils()}.
092 *
093 * @author {@link.uri mailto:ball@hcf.dev Allen D. Ball}
094 * @version $Revision: 6125 $
095 */
096@NoArgsConstructor(access = PROTECTED) @ToString
097public abstract class AbstractProcessor
098                      extends javax.annotation.processing.AbstractProcessor
099                      implements JavaLangReflectMethods {
100    /** See {@link JavacTask#instance(ProcessingEnvironment)}. */
101    protected JavacTask javac = null;
102    /** {@link JavacTask} {@link JavaFileManager} instance. */
103    protected JavaFileManager fm = null;
104    /** See {@link Trees#instance(ProcessingEnvironment)}. */
105    protected Trees trees = null;
106    /** See {@link ProcessingEnvironment#getFiler()}. */
107    protected Filer filer = null;
108    /** See {@link ProcessingEnvironment#getElementUtils()}. */
109    protected Elements elements = null;
110    /** See {@link ProcessingEnvironment#getTypeUtils()}. */
111    protected Types types = null;
112
113    @Override
114    public SourceVersion getSupportedSourceVersion() {
115        SourceVersion[] values = SourceVersion.values();
116
117        return values[values.length - 1];
118    }
119
120    @Override
121    public void init(ProcessingEnvironment processingEnv) {
122        super.init(processingEnv);
123
124        try {
125            filer = processingEnv.getFiler();
126            elements = processingEnv.getElementUtils();
127            types = processingEnv.getTypeUtils();
128
129            javac = JavacTask.instance(processingEnv);
130
131            if (javac != null) {
132                javac.addTaskListener(new WhenAnnotationProcessingFinished());
133            }
134
135            if (processingEnv instanceof JavacProcessingEnvironment) {
136                Context context =
137                    ((JavacProcessingEnvironment) processingEnv).getContext();
138
139                if (context != null) {
140                    fm = context.get(JavaFileManager.class);
141                }
142            }
143
144            trees = Trees.instance(processingEnv);
145        } catch (Exception exception) {
146            print(ERROR, exception);
147        }
148    }
149
150    @Override
151    public abstract boolean process(Set<? extends TypeElement> annotations,
152                                    RoundEnvironment roundEnv);
153
154    /**
155     * Callback method to signal
156     * {@link com.sun.source.util.TaskEvent.Kind#ANNOTATION_PROCESSING} is
157     * finished.
158     */
159    protected void whenAnnotationProcessingFinished() { }
160
161    /**
162     * See {@link JavaFileManager#getClassLoader(javax.tools.JavaFileManager.Location) JavaFileManager.getClassLoader(CLASS_PATH)}.
163     *
164     * @param   fm              The {@link JavaFileManager}.
165     *
166     * @return  The {@link ClassLoader}.
167     */
168    protected ClassLoader getClassPathClassLoader(JavaFileManager fm) {
169        ClassLoader loader = null;
170
171        if (fm != null) {
172            loader = fm.getClassLoader(CLASS_PATH);
173
174            if (loader instanceof URLClassLoader) {
175                loader =
176                    URLClassLoader
177                    .newInstance(((URLClassLoader) loader).getURLs(),
178                                 getClass().getClassLoader());
179            }
180        }
181
182        return loader;
183    }
184
185    /**
186     * Method to get a {@link TypeElement} for a {@link Class}.
187     *
188     * @param   type            The {@link Class}.
189     *
190     * @return  The {@link TypeElement} for the {@link Class}.
191     */
192    protected TypeElement asTypeElement(Class<?> type) {
193        TypeElement element = null;
194
195        try {
196            element = elements.getTypeElement(type.getCanonicalName());
197        } catch (Exception exception) {
198            throw new IllegalArgumentException("type=" + type, exception);
199        }
200
201        return element;
202    }
203
204    /**
205     * Method to get a {@link TypeMirror} for a {@link Class}.
206     *
207     * @param   type            The {@link Class}.
208     *
209     * @return  The {@link TypeMirror} for the {@link Class}.
210     */
211    protected TypeMirror asTypeMirror(Class<?> type) {
212        TypeMirror mirror = null;
213
214        if (type.isArray()) {
215            mirror = types.getArrayType(asTypeMirror(type.getComponentType()));
216        } else if (type.isPrimitive()) {
217            mirror = asTypeMirror(TypeKind.valueOf(type.getName().toUpperCase()));
218        } else {
219            mirror = asTypeElement(type).asType();
220        }
221
222        return mirror;
223    }
224
225    private TypeMirror asTypeMirror(TypeKind type) {
226        return type.isPrimitive() ? types.getPrimitiveType(type) : types.getNoType(type);
227    }
228
229    /**
230     * Method to get a {@link List} of {@link TypeMirror}s for an array of
231     * {@link Class}es.
232     *
233     * @param   types           The array of {@link Class}es.
234     *
235     * @return  The {@link List} of {@link TypeMirror}s.
236     */
237    protected List<TypeMirror> asTypeMirrorList(Class<?>... types) {
238        return Stream.of(types).map(t -> asTypeMirror(t)).collect(toList());
239    }
240
241    /**
242     * Constructor to get an {@link ExecutableElement} for a {@link Class}
243     * {@link java.lang.reflect.Constructor} by parameter list.
244     *
245     * @param   type            The {@link TypeElement}.
246     * @param   parameters      The constructor parameter types.
247     *
248     * @return  The {@link ExecutableElement} for the constructor.
249     */
250    protected ExecutableElement getConstructor(TypeElement type,
251                                               List<TypeMirror> parameters) {
252        Element element =
253            constructorsIn(type.getEnclosedElements())
254            .stream()
255            .filter(hasSameSignatureAs(parameters))
256            .findFirst().orElse(null);
257
258        return (ExecutableElement) element;
259    }
260
261    /**
262     * Method to get an {@link ExecutableElement} for a {@link Method}
263     * prototype.
264     *
265     * @param   method          The prototype {@link Method}.
266     *
267     * @return  The {@link ExecutableElement} for the method.
268     */
269    protected ExecutableElement getMethod(Method method) {
270        return getMethod(asTypeElement(method.getDeclaringClass()), method);
271    }
272
273    /**
274     * Method to get an {@link ExecutableElement} for a {@link Method}
275     * prototype.
276     *
277     * @param   type            The {@link TypeElement}.
278     * @param   method          The prototype {@link Method}.
279     *
280     * @return  The {@link ExecutableElement} for the method.
281     */
282    protected ExecutableElement getMethod(TypeElement type, Method method) {
283        Element element =
284            methodsIn(type.getEnclosedElements())
285            .stream()
286            .filter(hasSameSignatureAs(method))
287            .findFirst().orElse(null);
288
289        return (ExecutableElement) element;
290    }
291
292    /**
293     * Method to return the {@link ExecutableElement}
294     * ({@link java.lang.reflect.Method}) the argument
295     * {@link ExecutableElement} overrides (if any).
296     *
297     * @param   overrider       The {@link ExecutableElement}.
298     *
299     * @return  The overridden {@link ExecutableElement} if any;
300     *          {@code null} otherwise.
301     *
302     * @see Elements#overrides(ExecutableElement,ExecutableElement,TypeElement)
303     */
304    protected ExecutableElement overrides(ExecutableElement overrider) {
305        TypeElement type = (TypeElement) overrider.getEnclosingElement();
306        ExecutableElement element =
307            types.directSupertypes(type.asType())
308            .stream()
309            .map(t -> overrides(overrider, types.asElement(t)))
310            .filter(Objects::nonNull)
311            .findFirst().orElse(null);
312
313        return element;
314    }
315
316    private ExecutableElement overrides(ExecutableElement overrider,
317                                        Element type) {
318        ExecutableElement overridden = null;
319
320        if (type != null) {
321            switch (type.getKind()) {
322            case CLASS:
323            case INTERFACE:
324                overridden = overridden(overrider, (TypeElement) type);
325                break;
326
327            default:
328                break;
329            }
330        }
331
332        return overridden;
333    }
334
335    private ExecutableElement overridden(ExecutableElement overrider,
336                                         TypeElement type) {
337        ExecutableElement element =
338            methodsIn(type.getEnclosedElements())
339            .stream()
340            .filter(withoutModifiers(PRIVATE, STATIC))
341            .filter(t -> elements.overrides(overrider, t, type))
342            .findFirst().orElse(null);
343
344        if (element == null) {
345            element =
346                overrides(overrider, types.asElement(type.getSuperclass()));
347        }
348
349        return element;
350    }
351
352    /**
353     * Method to determine if a {@link ExecutableElement}
354     * ({@link java.lang.reflect.Method}) overrides another
355     * {@link ExecutableElement}.
356     *
357     * @param   overrider       The (possibly) overriding
358     *                          {@link ExecutableElement}.
359     * @param   overridden      The overridden {@link ExecutableElement}.
360     *
361     * @return  {@code true} if {@code overrider} overrides
362     *          {@code overridden}; {@code false} otherwise.
363     *
364     * @see Elements#overrides(ExecutableElement,ExecutableElement,TypeElement)
365     */
366    protected boolean overrides(ExecutableElement overrider,
367                                ExecutableElement overridden) {
368        TypeElement type = (TypeElement) overridden.getEnclosingElement();
369
370        return elements.overrides(overrider, overridden, type);
371    }
372
373    /**
374     * Method to return the {@link ExecutableElement}
375     * ({@link java.lang.reflect.Method}) the argument
376     * {@link ExecutableElement} is overriden by (if any).
377     *
378     * @param   overridden      The {@link ExecutableElement}.
379     * @param   type            The {@link TypeElement}.
380     *
381     * @return  The overriding {@link ExecutableElement} if any;
382     *          {@code null} otherwise.
383     *
384     * @see #overrides(ExecutableElement)
385     */
386    protected ExecutableElement implementationOf(ExecutableElement overridden,
387                                                 TypeElement type) {
388        ExecutableElement element = null;
389
390        if (type != null) {
391            element =
392                methodsIn(type.getEnclosedElements())
393                .stream()
394                .filter(t -> overrides(t, overridden))
395                .findFirst().orElse(null);
396
397            if (element == null) {
398                element =
399                    Optional.ofNullable(type.getSuperclass())
400                    .map(t -> (TypeElement) types.asElement(t))
401                    .filter(Objects::nonNull)
402                    .map(t -> implementationOf(overridden, t))
403                    .orElse(null);
404            }
405        }
406
407        return element;
408    }
409
410    /**
411     * Method to return the {@link ExecutableElement}
412     * ({@link java.lang.reflect.Method}) the argument
413     * {@link ExecutableElement} is specified by (if any).
414     *
415     * @param   method          The {@link ExecutableElement}.
416     *
417     * @return  The specification {@link ExecutableElement} if any;
418     *          {@code null} otherwise.
419     *
420     * @see #overrides(ExecutableElement)
421     */
422    protected ExecutableElement specifiedBy(ExecutableElement method) {
423        ExecutableElement specification = overrides(method);
424
425        if (specification != null) {
426            for (;;) {
427                ExecutableElement overridden = overrides(specification);
428
429                if (overridden != null) {
430                    specification = overridden;
431                } else {
432                    break;
433                }
434            }
435        }
436
437        return specification;
438    }
439
440    /**
441     * Method to get an {@link Element}'s {@link AnnotationMirror}.
442     *
443     * @param   element         The annotated {@link Element}.
444     * @param   type            The {@link Annotation} type ({@link Class}).
445     *
446     * @return  The {@link AnnotationMirror} if the {@link Element} is
447     *          annotated with the argument annotation; {@code null}
448     *          otherwise.
449     *
450     * @see Element#getAnnotationMirrors()
451     */
452    protected AnnotationMirror getAnnotationMirror(Element element,
453                                                   Class<? extends Annotation> type) {
454        return getAnnotationMirror(element, type.getName());
455    }
456
457    /**
458     * Method to get an {@link Element}'s {@link AnnotationMirror}.
459     *
460     * @param   element         The annotated {@link Element}.
461     * @param   type            The {@link Annotation} type
462     *                          ({@link TypeElement}).
463     *
464     * @return  The {@link AnnotationMirror} if the {@link Element} is
465     *          annotated with the argument annotation; {@code null}
466     *          otherwise.
467     *
468     * @see Element#getAnnotationMirrors()
469     */
470    protected AnnotationMirror getAnnotationMirror(Element element,
471                                                   TypeElement type) {
472        return getAnnotationMirror(element,
473                                   type.getQualifiedName().toString());
474    }
475
476    private AnnotationMirror getAnnotationMirror(Element element,
477                                                 String name) {
478        AnnotationMirror mirror =
479            element.getAnnotationMirrors()
480            .stream()
481            .filter(t -> t.getAnnotationType().toString().equals(name))
482            .map(t -> (AnnotationMirror) t)
483            .findFirst().orElse(null);
484
485        return mirror;
486    }
487
488    /**
489     * Method to get an {@link AnnotationMirror} element's
490     * {@link AnnotationValue}.
491     *
492     * @param   annotation      The {@link AnnotationMirror}.
493     * @param   name            The simple name of the element.
494     *
495     * @return  The {@link AnnotationValue} if it is defined; {@code null}
496     *          otherwise.
497     *
498     * @see Elements#getElementValuesWithDefaults(AnnotationMirror)
499     */
500    protected AnnotationValue getAnnotationValue(AnnotationMirror annotation, String name) {
501        AnnotationValue value =
502            elements.getElementValuesWithDefaults(annotation).entrySet()
503            .stream()
504            .filter(t -> named(name).test(t.getKey()))
505            .map(t -> t.getValue())
506            .findFirst().orElse(null);
507
508        return value;
509    }
510
511    /**
512     * Method to determine if an {@link AnnotationValue} is "empty":
513     * {@code null} or an empty array.
514     *
515     * @param   value           The {@link AnnotationValue}.
516     *
517     * @return  {@code true} if empty; {code false} otherwise.
518     */
519    protected boolean isEmptyArray(AnnotationValue value) {
520        List<?> list = (List<?>) ((value != null) ? value.getValue() : null);
521
522        return (list == null || list.isEmpty());
523    }
524
525    /**
526     * Method to get bean property name from an {@link ExecutableElement}.
527     *
528     * @param   element         The {@link ExecutableElement}.
529     *
530     * @return  the name {@link String} if the {@link ExecutableElement}
531     *          is a getter or setter method; {@code null} otherwise.
532     */
533    protected String getPropertyName(ExecutableElement element) {
534        String string =
535            Stream.of(PropertyMethodEnum.values())
536            .filter(t -> t.getPropertyName(element.getSimpleName().toString()) != null)
537            .filter(t -> isAssignableTo(t.getReturnType(),
538                                        e -> ((ExecutableElement) e).getReturnType()).test(element))
539            .filter(t -> withParameters(t.getParameterTypes()).test(element))
540            .map(t -> t.getPropertyName(element.getSimpleName().toString()))
541            .findFirst().orElse(null);
542
543        return string;
544    }
545
546    /**
547     * Method to determine if an {@link ExecutableElement} is a bean getter.
548     *
549     * @param   element         The {@link ExecutableElement}.
550     *
551     * @return  {@code true} if the {@link Element} has a non-private getter
552     *          method; {@code false} otherwise.
553     */
554    protected boolean isGetterMethod(ExecutableElement element) {
555        Optional <PropertyMethodEnum> optional =
556            Stream.of(PropertyMethodEnum.GET, PropertyMethodEnum.IS)
557            .filter(t -> t.getPropertyName(element.getSimpleName().toString()) != null)
558            .filter(t -> withoutModifiers(PRIVATE).test(element))
559            .filter(t -> isAssignableTo(t.getReturnType(),
560                                        e -> ((ExecutableElement) e).getReturnType()).test(element))
561            .filter(t -> withParameters(t.getParameterTypes()).test(element))
562            .findFirst();
563
564        return optional.isPresent();
565    }
566
567    /**
568     * Method to get the {@link Set} of bean property names for the
569     * specified {@link TypeElement}.
570     *
571     * @param   type            The {@link TypeElement} to analyze.
572     *
573     * @return  The {@link Set} of bean property names.
574     */
575    protected Set<String> getPropertyNames(TypeElement type) {
576        return getPropertyNames(new TreeSet<>(), type);
577    }
578
579    private Set<String> getPropertyNames(Set<String> set, TypeElement type) {
580        for (ExecutableElement element :
581                 methodsIn(type.getEnclosedElements())) {
582            if (withoutModifiers(PRIVATE).test(element)) {
583                Stream.of(PropertyMethodEnum.values())
584                    .filter(t -> t.getPropertyName(element.getSimpleName().toString()) != null)
585                    .filter(t -> isAssignableTo(t.getReturnType(),
586                                                e -> ((ExecutableElement) e).getReturnType()).test(element))
587                    .filter(t -> withParameters(t.getParameterTypes()).test(element))
588                    .map(t -> t.getPropertyName(element.getSimpleName().toString()))
589                    .forEach(t -> set.add(t));
590            }
591        }
592
593        Element superclass = types.asElement(type.getSuperclass());
594
595        if (superclass != null)
596            switch (superclass.getKind()) {
597            case CLASS:
598                getPropertyNames(set, (TypeElement) superclass);
599                break;
600
601            default:
602                break;
603        }
604
605        return set;
606    }
607    /*
608     * Element Predicate Calculus
609     */
610    protected <E extends Enum<E>> EnumSet<E> toEnumSet(E[] array) {
611        return EnumSet.copyOf(Arrays.asList(array));
612    }
613
614    private <E extends Enum<E>> Predicate<Element> is(E e, Function<? super Element,E> extractor) {
615        return t -> e.equals(extractor.apply(t));
616    }
617
618    protected Predicate<Element> hasSameSignatureAs(List<TypeMirror> parameters) {
619        return is(CONSTRUCTOR, Element::getKind).and(withParameters(parameters));
620    }
621
622    protected Predicate<Element> hasSameSignatureAs(Method method) {
623        return hasSameSignatureAs(method.getName(),
624                                  method.getParameterTypes());
625    }
626
627    protected Predicate<Element> hasSameSignatureAs(CharSequence name,
628                                                    Class<?>[] parameters) {
629        return is(METHOD, Element::getKind).and(named(name).and(withParameters(parameters)));
630    }
631
632    protected Predicate<Element> isAssignableTo(Class<?> type) {
633        return isAssignableTo(type, t -> t.asType());
634    }
635
636    protected Predicate<Element> isAssignableTo(TypeMirror type) {
637        return isAssignableTo(type, t -> t.asType());
638    }
639
640    protected Predicate<Element> isAssignableTo(Class<?> type,
641                                                Function<? super Element,TypeMirror> extractor) {
642        return isAssignableTo(asTypeMirror(type), extractor);
643    }
644
645    protected Predicate<Element> isAssignableTo(TypeMirror type,
646                                                Function<? super Element,TypeMirror> extractor) {
647        return t -> types.isAssignable(extractor.apply(t), type);
648    }
649
650    protected Predicate<Element> named(CharSequence name) {
651        return t -> t.getSimpleName().contentEquals(name);
652    }
653
654    protected Predicate<Element> withParameters(Class<?>[] parameters) {
655        return withParameters(asTypeMirrorList(parameters));
656    }
657
658    protected Predicate<Element> withParameters(List<TypeMirror> parameters) {
659        return new Predicate<Element>() {
660            @Override
661            public boolean test(Element element) {
662                boolean match =
663                    parameters.size() == ((ExecutableElement) element).getParameters().size();
664
665                if (match) {
666                    match &=
667                        IntStream.range(0, parameters.size())
668                        .allMatch(i -> isAssignableTo(parameters.get(i),
669                                                      t -> types.erasure(((ExecutableElement) t)
670                                                                         .getParameters().get(i).asType()))
671                                       .test(element));
672                }
673
674                return match;
675            }
676        };
677    }
678
679    protected Predicate<Element> withModifiers(Modifier... modifiers) {
680        return withModifiers(toEnumSet(modifiers));
681    }
682
683    protected Predicate<Element> withModifiers(Set<Modifier> modifiers) {
684        return with(modifiers, t -> t.getModifiers());
685    }
686
687    protected Predicate<Element> withoutModifiers(Modifier... modifiers) {
688        return withoutModifiers(toEnumSet(modifiers));
689    }
690
691    protected Predicate<Element> withoutModifiers(Set<Modifier> modifiers) {
692        return without(modifiers, t -> t.getModifiers());
693    }
694
695    protected <E> Predicate<Element> with(Set<E> set,
696                                          Function<Element,Collection<E>> extractor) {
697        return t -> extractor.apply(t).containsAll(set);
698    }
699
700    protected <E> Predicate<Element> without(Set<E> set,
701                                             Function<Element,Collection<E>> extractor) {
702        return t -> disjoint(set, extractor.apply(t));
703    }
704
705    /**
706     * Method to print a diagnostic message.
707     *
708     * @param   kind            The {@link javax.tools.Diagnostic.Kind}.
709     * @param   format          The message format {@link String}.
710     * @param   argv            Optional arguments to the message format
711     *                          {@link String}.
712     *
713     * @see javax.annotation.processing.Messager#printMessage(Diagnostic.Kind,CharSequence)
714     */
715    protected void print(Diagnostic.Kind kind, String format, Object... argv) {
716        processingEnv.getMessager()
717            .printMessage(kind, String.format(format, argv));
718    }
719
720    /**
721     * Method to print a diagnostic message.
722     *
723     * @param   kind            The {@link javax.tools.Diagnostic.Kind}.
724     * @param   element         The offending {@link Element}.
725     * @param   format          The message format {@link String}.
726     * @param   argv            Optional arguments to the message format
727     *                          {@link String}.
728     *
729     * @see javax.annotation.processing.Messager#printMessage(Diagnostic.Kind,CharSequence,Element)
730     */
731    protected void print(Diagnostic.Kind kind, Element element,
732                         String format, Object... argv) {
733        processingEnv.getMessager()
734            .printMessage(kind, String.format(format, argv),
735                          element);
736    }
737
738    /**
739     * Method to print a diagnostic message.
740     *
741     * @param   kind            The {@link javax.tools.Diagnostic.Kind}.
742     * @param   element         The offending {@link Element}.
743     * @param   annotation      The offending {@link AnnotationMirror}.
744     * @param   format          The message format {@link String}.
745     * @param   argv            Optional arguments to the message format
746     *                          {@link String}.
747     *
748     * @see javax.annotation.processing.Messager#printMessage(Diagnostic.Kind,CharSequence,Element,AnnotationMirror)
749     */
750    protected void print(Diagnostic.Kind kind,
751                         Element element, AnnotationMirror annotation,
752                         String format, Object... argv) {
753        processingEnv.getMessager()
754            .printMessage(kind, String.format(format, argv),
755                          element, annotation);
756    }
757
758    /**
759     * Method to print a diagnostic message.
760     *
761     * @param   kind            The {@link javax.tools.Diagnostic.Kind}.
762     * @param   element         The offending {@link Element}.
763     * @param   annotation      The offending {@link AnnotationMirror}.
764     * @param   value           The offending {@link AnnotationValue}.
765     * @param   format          The message format {@link String}.
766     * @param   argv            Optional arguments to the message format
767     *                          {@link String}.
768     *
769     * @see javax.annotation.processing.Messager#printMessage(Diagnostic.Kind,CharSequence,Element,AnnotationMirror,AnnotationValue)
770     */
771    protected void print(Diagnostic.Kind kind,
772                         Element element,
773                         AnnotationMirror annotation, AnnotationValue value,
774                         String format, Object... argv) {
775        processingEnv.getMessager()
776            .printMessage(kind, String.format(format, argv),
777                          element, annotation, value);
778    }
779
780    /**
781     * Method to print a {@link Throwable}.
782     *
783     * @param   kind            The {@link javax.tools.Diagnostic.Kind}.
784     * @param   throwable       The {@link Throwable}.
785     */
786    protected void print(Diagnostic.Kind kind, Throwable throwable) {
787        print(kind, new ThrowableDataSource(throwable).toString());
788    }
789
790    /**
791     * Abstract {@link Criterion} base class.
792     */
793    @NoArgsConstructor(access = PROTECTED) @ToString
794    protected abstract class Criterion<T extends Element> implements Predicate<T> { }
795
796    /**
797     * Abstract {@link Check} base class.
798     */
799    @NoArgsConstructor(access = PROTECTED) @ToString
800    protected abstract class Check<T extends Element> implements Consumer<T> { }
801
802    @NoArgsConstructor @ToString
803    private class WhenAnnotationProcessingFinished extends AbstractTaskListener {
804        @Override
805        public void finished(TaskEvent event) {
806            switch (event.getKind()) {
807            case ANNOTATION_PROCESSING:
808                javac.removeTaskListener(this);
809                whenAnnotationProcessingFinished();
810                break;
811
812            default:
813                break;
814            }
815        }
816    }
817}