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