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