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