001/* -*- mode: Java; c-basic-offset: 2; indent-tabs-mode: nil; coding: utf-8-unix -*-
002 *
003 * Copyright © 2024–2025 microBean™.
004 *
005 * Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance with
006 * the License. You may obtain a copy of the License at
007 *
008 *     http://www.apache.org/licenses/LICENSE-2.0
009 *
010 * Unless required by applicable law or agreed to in writing, software distributed under the License is distributed on
011 * an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the License for the
012 * specific language governing permissions and limitations under the License.
013 */
014package org.microbean.construct;
015
016import java.lang.reflect.Constructor;
017import java.lang.reflect.Executable;
018import java.lang.reflect.GenericArrayType;
019import java.lang.reflect.GenericDeclaration;
020import java.lang.reflect.Method;
021import java.lang.reflect.ParameterizedType;
022import java.lang.reflect.Type;
023
024import java.util.List;
025import java.util.Objects;
026
027import javax.lang.model.element.Element;
028import javax.lang.model.element.ElementKind;
029import javax.lang.model.element.ExecutableElement;
030import javax.lang.model.element.ModuleElement;
031import javax.lang.model.element.Name;
032import javax.lang.model.element.PackageElement;
033import javax.lang.model.element.Parameterizable;
034import javax.lang.model.element.QualifiedNameable;
035import javax.lang.model.element.RecordComponentElement;
036import javax.lang.model.element.TypeElement;
037import javax.lang.model.element.TypeParameterElement;
038import javax.lang.model.element.VariableElement;
039
040import javax.lang.model.type.ArrayType;
041import javax.lang.model.type.DeclaredType;
042import javax.lang.model.type.ExecutableType;
043import javax.lang.model.type.NoType;
044import javax.lang.model.type.NullType;
045import javax.lang.model.type.PrimitiveType;
046import javax.lang.model.type.TypeKind;
047import javax.lang.model.type.TypeMirror;
048import javax.lang.model.type.TypeVariable;
049import javax.lang.model.type.WildcardType;
050
051import javax.lang.model.util.Elements;
052import javax.lang.model.util.Elements.Origin;
053
054import org.microbean.construct.element.StringName;
055import org.microbean.construct.element.UniversalElement;
056
057import org.microbean.construct.type.UniversalType;
058
059import static javax.lang.model.element.ElementKind.CONSTRUCTOR;
060import static javax.lang.model.element.ElementKind.METHOD;
061
062/**
063 * A representation of a domain of valid Java constructs.
064 *
065 * <p>A <dfn id="domain">domain</dfn> is a set of valid Java <a href="#construct">constructs</a>. A {@link Domain}
066 * provides access to a domain and its members.</p>
067 *
068 * <p>A Java <dfn id="construct">construct</dfn> is either a <a href="#type">type</a> or an <a
069 * href="#element">element</a>.</p>
070 *
071 * <p>A <dfn id="type">type</dfn> is a usage of a Java type, most commonly represented as a {@link TypeMirror}.</p>
072 *
073 * <p>An <dfn id="element">element</dfn> ia a declaration of a Java program element, most commonly represented as an
074 * {@link Element}.</p>
075 *
076 * <p>Domains impose constraints on the <a href="#type">types</a> and <a href="#element">elements</a> they contain, and
077 * on the kinds and semantics of operations that can be performed on them.</p>
078 *
079 * <p>This interface is modeled on a deliberately restricted combination of the {@link javax.lang.model.util.Elements}
080 * and {@link javax.lang.model.util.Types} interfaces.</p>
081 *
082 * <p>{@link Domain} implementations must be thread-safe.</p>
083 *
084 * @author <a href="https://about.me/lairdnelson" target="_top">Laird Nelson</a>
085 */
086@SuppressWarnings("try")
087public interface Domain {
088
089  /**
090   * Returns an {@link ArrayType} whose {@linkplain ArrayType#getComponentType() component type} is {@linkplain
091   * #sameType(TypeMirror, TypeMirror) the same as} the supplied {@code componentType}.
092   *
093   * @param componentType the component type; must not be {@code null}
094   *
095   * @return a non-{@code null} {@link ArrayType} whose {@linkplain ArrayType#getComponentType() component type} is
096   * {@linkplain #sameType(TypeMirror, TypeMirror) the same as} the supplied {@code componentType}
097   *
098   * @exception NullPointerException if {@code componentType} is {@code null}
099   *
100   * @exception IllegalArgumentException if {@code componentType} is not a valid component type
101   *
102   * @see javax.lang.model.util.Types#getArrayType(TypeMirror)
103   */
104  public ArrayType arrayTypeOf(final TypeMirror componentType);
105
106  /**
107   * Returns a non-{@code null} {@link TypeMirror} representing the type of the supplied {@link Element} when that
108   * {@link Element} is viewed as a member of, or otherwise directly contained by, the supplied {@code containingType}.
109   *
110   * <p>For example, when viewed as a member of the parameterized type {@link java.util.Set Set&lt;String&gt;}, the
111   * {@link java.util.Set#add(Object)} method (represented as an {@link ExecutableElement}) {@linkplain
112   * ExecutableElement#asType() has} a {@linkplain ExecutableType type} whose {@linkplain
113   * ExecutableType#getParameterTypes() method parameter is of type} {@link String} (not {@link String}'s erasure).</p>
114   *
115   * @param containingType the containing {@link DeclaredType}; must not be {@code null}
116   *
117   * @param e the member {@link Element}; must not be {@code null}
118   *
119   * @return a non-{@code null} {@linkplain TypeMirror type} representing the {@linkplain Element#asType() type of} the
120   * supplied {@link Element} when viewed as a member of the supplied {@code containingType}; never {@code null}
121   *
122   * @exception NullPointerException if either {@code containingType} or {@code e} is {@code null}
123   *
124   * @exception IllegalArgumentException if {@code e} cannot be viewed as a member of the supplied {@code
125   * containingType} (because it is the wrong {@linkplain Element#getKind() kind}, for example)
126   *
127   * @see javax.lang.model.util.Types#asMemberOf(DeclaredType, Element)
128   */
129  public TypeMirror asMemberOf(final DeclaredType containingType, final Element e);
130
131  /**
132   * Returns {@code true} if and only if the supplied {@code payload} (the first argument) is considered assignable to
133   * the supplied {@code receiver} (the second argument) according to <a
134   * href="https://docs.oracle.com/javase/specs/jls/se21/html/jls-5.html#jls-5.2">the rules of the Java Language
135   * Specification</a>.
136   *
137   * @param payload the {@link TypeMirror} being assigned; must not be {@code null}
138   *
139   * @param receiver the {@link TypeMirror} receiving the assignment; must not be {@code null}
140   *
141   * @return {@code true} if and only if {@code payload} is assignable to {@code receiver}
142   *
143   * @exception NullPointerException if either {@code payload} or {@code receiver} is {@code null}
144   *
145   * @exception IllegalArgumentException if either {@link TypeMirror} is not one that can take part in an assignment
146   *
147   * @see javax.lang.model.util.Types#isAssignable(TypeMirror, TypeMirror)
148   *
149   * @spec https://docs.oracle.com/javase/specs/jls/se21/html/jls-5.html#jls-5.2 Java Language Specification, section
150   * 5.2
151   */
152  // Note the strange positioning of payload and receiver.
153  public boolean assignable(final TypeMirror payload, final TypeMirror receiver);
154
155  /**
156   * Returns the (non-{@code null}) <a
157   * href="https://docs.oracle.com/javase/specs/jls/se21/html/jls-13.html#jls-13.1"><dfn>binary name</dfn></a> of the
158   * supplied {@link TypeElement}.
159   *
160   * @param e a {@link TypeElement}; must not be {@code null}
161   *
162   * @return a non-{@code null} {@link Name}
163   *
164   * @exception NullPointerException if {@code e} is {@code null}
165   *
166   * @see javax.lang.model.util.Elements#getBinaryName(TypeElement)
167   *
168   * @spec https://docs.oracle.com/javase/specs/jls/se21/html/jls-13.html#jls-13.1 Java Language Specification, section
169   * 13.1
170   */
171  public Name binaryName(final TypeElement e);
172
173  /**
174   * Returns {@code true} if and only if the supplied {@link ExecutableElement} represents a <dfn>bridge method</dfn>.
175   *
176   * @param e an {@link ExecutableElement}; must not be {@code null}
177   *
178   * @return {@code true} if and only if the supplied {@link ExecutableElement} represents a bridge method
179   *
180   * @exception NullPointerException if {@code e} is {@code null}
181   *
182   * @see javax.lang.model.util.Elements#isBridge(ExecutableElement)
183   *
184   * @spec https://docs.oracle.com/javase/specs/jls/se23/html/jls-8.html#jls-8.4.8.3 Java Language Specification,
185   * section 8.4.8.3
186   *
187   * @spec https://docs.oracle.com/javase/specs/jls/se23/html/jls-15.html#jls-15.12.4.5 Java Language Specification,
188   * section 15.12.4.5
189   */
190  public boolean bridge(final ExecutableElement e);
191
192  /**
193   * Applies <a href="https://docs.oracle.com/javase/specs/jls/se23/html/jls-5.html#jls-5.1.10"><dfn>capture
194   * conversion</dfn></a> to the supplied {@link TypeMirror}, which is normally a {@linkplain TypeKind#WILDCARD wildcard
195   * type}.
196   *
197   * @param wildcard a {@link TypeMirror}; must not be {@code null}; if not a {@linkplain TypeKind#WILDCARD wildcard
198   * type}, then it will be returned unchanged
199   *
200   * @return a non-{@code null} {@link TypeMirror} representing the result of <a
201   * href="https://docs.oracle.com/javase/specs/jls/se23/html/jls-5.html#jls-5.1.10">capture conversion</a> applied to
202   * the supplied {@link TypeMirror}
203   *
204   * @exception NullPointerException if {@code wildcard} is {@code null}
205   *
206   * @see javax.lang.model.util.Types#capture(TypeMirror)
207   *
208   * @spec https://docs.oracle.com/javase/specs/jls/se23/html/jls-5.html#jls-5.1.10 Java Language Specification, section
209   * 5.1.10
210   */
211  public TypeMirror capture(final TypeMirror wildcard);
212
213  /**
214   * Returns {@code true} if and only if {@code candidateContainer} <dfn>contains</dfn> {@code candidate}, according to
215   * the <a href="https://docs.oracle.com/javase/specs/jls/se23/html/jls-4.html#jls-4.5.1">Java Language Specification,
216   * section 4.5.1</a>.
217   *
218   * @param candidateContainer the putative containing type; normally a {@linkplain TypeKind#WILDCARD wildcard type};
219   * must not be {@code null}
220   *
221   * @param candidate the putative contained type; must not be {@code null}
222   *
223   * @return {@code true} if and only if {@code candidateContainer} <dfn>contains</dfn> {@code candidate}, according to
224   * the <a href="https://docs.oracle.com/javase/specs/jls/se23/html/jls-4.html#jls-4.5.1">Java Language Specification,
225   * section 4.5.1</a>; {@code false} otherwise
226   *
227   * @exception NullPointerException if either argument is {@code null}
228   *
229   * @exception IllegalArgumentException if either argument is either an {@linkplain TypeKind#EXECUTABLE executable
230   * type}, a {@linkplain TypeKind#MODULE module type}, or a {@linkplain TypeKind#PACKAGE package type}
231   *
232   * @spec https://docs.oracle.com/javase/specs/jls/se23/html/jls-4.html#jls-4.5.1 Java Language Specification, section
233   * 4.5.1
234   */
235  public boolean contains(final TypeMirror candidateContainer, final TypeMirror candidate);
236
237  /**
238   * A convenience method that returns the {@link DeclaredType} {@linkplain TypeElement#asType() of} a {@link
239   * TypeElement} that bears the supplied {@code canonicalName}, <strong>or {@code null} if there is no such {@link
240   * TypeElement} (and therefore no such {@link DeclaredType})</strong>.
241   *
242   * @param canonicalName a valid canonical name; must not be {@code null}
243   *
244   * @return a {@link DeclaredType} with a {@link TypeKind} of {@link TypeKind#DECLARED}, or {@code null}
245   *
246   * @see #typeElement(CharSequence)
247   *
248   * @see #declaredType(TypeElement, TypeMirror...)
249   *
250   * @spec https://docs.oracle.com/javase/specs/jls/se23/html/jls-6.html#jls-6.7 Java Language Specification, section
251   * 6.7
252   */
253  public default DeclaredType declaredType(final CharSequence canonicalName) {
254    final TypeElement e = this.typeElement(canonicalName);
255    return e == null ? null : this.declaredType(e);
256  }
257
258  /**
259   * Returns the {@link DeclaredType} {@linkplain TypeElement#asType() of} the supplied {@link TypeElement} with the
260   * supplied {@link TypeMirror} arguments (if any), yielding a parameterized type.
261   *
262   * <p>Given a {@link TypeElement} representing the class named {@link java.util.Set java.util.Set} and a {@link
263   * TypeMirror} representing the type declared by {@link String java.lang.String}, for example, this method will return
264   * a {@link DeclaredType} representing the parameterized type corresponding to {@link java.util.Set
265   * java.util.Set&lt;java.lang.String&gt;}.</p>
266   *
267   * <p>The number of supplied type arguments must either equal the number of the supplied {@link TypeElement}'s
268   * {@linkplain TypeElement#getTypeParameters() formal type parameters}, or must be zero. If it is zero, and if the
269   * supplied {@link TypeElement} {@link #generic(Element) is generic}, then the supplied {@link TypeElement}'s raw type
270   * is returned.</p>
271   *
272   * <p>If a parameterized type is returned, {@linkplain DeclaredType#asElement() its <code>TypeElement</code>} must not
273   * be contained within a {@linkplain #generic(Element) generic} outer class. The parameterized type {@code
274   * Outer<String>.Inner<Number>}, for example, may be constructed by first using this method to get the type {@code
275   * Outer<String>}, and then invoking {@link #declaredType(DeclaredType, TypeElement, TypeMirror...)}.</p>
276   *
277   * @param typeElement a {@link TypeElement}; must not be {@code null}
278   *
279   * @param typeArguments any type arguments (represented by {@link TypeMirror}s); must not be {@code null}
280   *
281   * @return a non-{@code null} {@link DeclaredType} with a {@link TypeKind} of {@link TypeKind#DECLARED}
282   *
283   * @exception NullPointerException if {@code typeElement} or {@code typeArguments} is {@code null}
284   *
285   * @see javax.lang.model.util.Types#getDeclaredType(TypeElement, TypeMirror...)
286   *
287   * @spec https://docs.oracle.com/javase/specs/jls/se23/html/jls-4.html#jls-4.5 Java Language Specification, section 4.5
288   */
289  public DeclaredType declaredType(final TypeElement typeElement,
290                                   final TypeMirror... typeArguments);
291
292  /**
293   * Returns the {@link DeclaredType} {@linkplain TypeElement#asType() of} the supplied {@link TypeElement} with the
294   * supplied {@link TypeMirror} arguments (if any), given a containing {@link DeclaredType} of which it is a member,
295   * yielding a parameterized type.
296   *
297   * <p>Given a {@link DeclaredType} representing the parameterized type corresponding to {@code Outer<}{@link
298   * String}{@code >} (see the {@link #declaredType(TypeElement, TypeMirror...)} method), a {@link TypeElement}
299   * representing the class named {@code Outer.Inner} and a {@link DeclaredType} representing the non-generic class
300   * corresponding to {@link Number}, for example, this method will return a {@link DeclaredType} representing the
301   * parameterized type corresponding to {@code Outer<}{@link String}{@code >}{@code .Inner<}{@link Number}{@code >}.</p>
302   *
303   * <p>The number of supplied type arguments must either equal the number of the supplied {@link TypeElement}'s
304   * {@linkplain TypeElement#getTypeParameters() formal type parameters}, or must be zero. If it is zero, and if the
305   * supplied {@link TypeElement} {@link #generic(Element) is generic}, then the supplied {@link TypeElement}'s raw type
306   * is returned.</p>
307   *
308   * @param enclosingType a {@link DeclaredType} representing the containing type; must not be {@code null}
309   *
310   * @param typeElement a {@link TypeElement}; must not be {@code null}
311   *
312   * @param typeArguments any type arguments (represented by {@link TypeMirror}s); must not be {@code null}
313   *
314   * @return a non-{@code null} {@link DeclaredType} with a {@link TypeKind} of {@link TypeKind#DECLARED}
315   *
316   * @exception NullPointerException if {@code enclosingType}, {@code typeElement}, or {@code typeArguments} is {@code
317   * null}
318   *
319   * @see #declaredType(TypeElement, TypeMirror...)
320   *
321   * @see javax.lang.model.util.Types#getDeclaredType(DeclaredType, TypeElement, TypeMirror...)
322   *
323   * @spec https://docs.oracle.com/javase/specs/jls/se23/html/jls-4.html#jls-4.5 Java Language Specification, section 4.5
324   */
325  public DeclaredType declaredType(final DeclaredType enclosingType,
326                                   final TypeElement typeElement,
327                                   final TypeMirror... typeArguments);
328
329  /**
330   * Returns a non-{@code null} {@link List} of the <dfn>direct supertypes</dfn> of the supplied {@link TypeMirror},
331   * which is normally a {@linkplain TypeKind#DECLARED declared type}.
332   *
333   * @param t a {@link TypeMirror}; must not be {@code null}; must not be an {@linkplain TypeKind#EXECUTABLE executable
334   * type}, a {@linkplain TypeKind#MODULE module type}, or a {@linkplain TypeKind#PACKAGE package type}
335   *
336   * @return a non-{@code null}, immutable {@link List} of {@link TypeMirror}s representing the direct supertypes
337   *
338   * @exception NullPointerException if {@code t} is {@code null}
339   *
340   * @exception IllegalArgumentException if either argument is either an {@linkplain TypeKind#EXECUTABLE executable
341   * type}, a {@linkplain TypeKind#MODULE module type}, or a {@linkplain TypeKind#PACKAGE package type}
342   *
343   * @see javax.lang.model.util.Types#directSupertypes(TypeMirror)
344   *
345   * @spec https://docs.oracle.com/javase/specs/jls/se23/html/jls-4.html#jls-4.10 Java Language Specification, section
346   * 4.10
347   */
348  public List<? extends TypeMirror> directSupertypes(final TypeMirror t);
349
350  /**
351   * Returns the {@link Element} responsible for declaring the supplied {@link TypeMirror}, which is most commonly a
352   * {@link DeclaredType}, a {@link TypeVariable}, a {@link NoType} with a {@link TypeKind} of {@link TypeKind#MODULE},
353   * or a {@link NoType} with a {@link TypeKind} of {@link TypeKind#PACKAGE}, <strong>or {@code null} if there is no
354   * such {@link Element}</strong>.
355   *
356   * @param t a {@link TypeMirror}; must not be {@code null}
357   *
358   * @return an {@link Element}, or {@code null}
359   *
360   * @exception NullPointerException if {@code t} is {@code null}
361   *
362   * @see javax.lang.model.util.Types#asElement(TypeMirror)
363   */
364  public Element element(final TypeMirror t);
365
366  /**
367   * A convenience method that returns the <dfn>element type</dfn> of the supplied {@link TypeMirror}.
368   *
369   * <p>The element type of an {@linkplain TypeKind#ARRAY array type} is the element type of its {@linkplain
370   * ArrayType#getComponentType() component type}.</p>.
371   *
372   * <p>The element type of every other kind of type is the type itself. Note that the semantics of the prior sentence
373   * diverge deliberately, primarily for convenience, from those of the relevant section in the Java Language
374   * Specification.</p>
375   *
376   * @param t a {@link TypeMirror}; must not be {@code null}
377   *
378   * @return the <dfn>element type</dfn> of the supplied {@link TypeMirror}; never {@code null}
379   *
380   * @exception NullPointerException if {@code t} is {@code null}
381   *
382   * @spec https://docs.oracle.com/javase/specs/jls/se23/html/jls-10.html#jls-10.1 Java Language Specification, section
383   * 10.1
384   */
385  public default TypeMirror elementType(final TypeMirror t) {
386    return switch (t) {
387    case null -> throw new NullPointerException("t");
388    case UniversalType ut -> ut.elementType();
389    default -> {
390      try (var lock = lock()) {
391        yield t.getKind() == TypeKind.ARRAY ? this.elementType(((ArrayType)t).getComponentType()) : t;
392      }
393    }
394    };
395  }
396
397  /**
398   * Returns the <dfn>erasure</dfn> of the supplied {@link TypeMirror}.
399   *
400   * @param <T> a {@link TypeMirror} specialization
401   *
402   * @param t the {@link TypeMirror} representing the type whose erasure should be returned; must not be {@code null}
403   *
404   * @return the erasure of the supplied {@link TypeMirror}; never {@code null}
405   *
406   * @exception NullPointerException if {@code t} is {@code null}
407   *
408   * @exception IllegalArgumentException if {@code t} is a {@link NoType} with a {@link TypeKind} of {@link
409   * TypeKind#MODULE}, or a {@link NoType} with a {@link TypeKind} of {@link TypeKind#PACKAGE}
410   *
411   * @see javax.lang.model.util.Types#erasure(TypeMirror)
412   *
413   * @spec https://docs.oracle.com/javase/specs/jls/se23/html/jls-4.html#jls-4.6 Java Language Specification, section
414   * 4.6
415   */
416  public <T extends TypeMirror> T erasure(final T t);
417
418  /**
419   * Returns an {@link ExecutableElement} corresponding to the supplied {@link Executable}.
420   *
421   * @param e an {@link Executable}; must not be {@code null}
422   *
423   * @return an {@link ExecutableElement} corresponding to the supplied {@link Executable}; never {@code null}
424   *
425   * @exception NullPointerException if {@code e} is {@code null}
426   *
427   * @exception IllegalArgumentException if somehow {@code e} is neither a {@link Constructor} nor a {@link Method}
428   */
429  public default ExecutableElement executableElement(final Executable e) {
430    return switch (e) {
431    case null -> throw new NullPointerException("e");
432    case Constructor<?> c ->
433      this.executableElement(this.typeElement(c.getDeclaringClass().getCanonicalName()),
434                             this.noType(TypeKind.VOID),
435                             "<init>",
436                             this.types(c.getParameterTypes()));
437    case Method m ->
438      this.executableElement(this.typeElement(m.getDeclaringClass().getCanonicalName()),
439                             this.type(m.getReturnType()),
440                             m.getName(),
441                             this.types(m.getParameterTypes()));
442    default -> throw new IllegalArgumentException("e: " + e);
443    };
444  }
445
446
447  /**
448   * A convenience method that returns an {@link ExecutableElement} representing the static initializer, constructor or
449   * method described by the supplied arguments, <strong>or {@code null} if no such {@link ExecutableElement}
450   * exists</strong>.
451   *
452   * @param declaringElement a {@link TypeElement} representing the class that declares the executable; must not be
453   * {@code null}
454   *
455   * @param returnType the {@linkplain ExecutableElement#getReturnType() return type} of the executable; must not be
456   * {@code null}
457   *
458   * @param name the {@linkplain ExecutableElement#getSimpleName() name} of the executable; must not be {@code null}
459   *
460   * @param parameterTypes {@link TypeMirror}s that represent the executable's {@linkplain
461   * ExecutableElement#getParameters() parameter types}
462   *
463   * @return an {@link ExecutableElement} with an {@link ElementKind} of {@link ElementKind#CONSTRUCTOR}, {@link
464   * ElementKind#METHOD}, or {@link ElementKind#STATIC_INIT}, or {@code null}
465   *
466   * @exception NullPointerException if any argument is {@code null}
467   */
468  public default ExecutableElement executableElement(final TypeElement declaringElement,
469                                                     final TypeMirror returnType,
470                                                     final CharSequence name,
471                                                     final TypeMirror... parameterTypes) {
472    return switch (declaringElement) {
473    case null -> throw new NullPointerException("declaringElement");
474    case UniversalElement ue -> {
475      final List<? extends UniversalElement> ees = ue.getEnclosedElements();
476      yield ees.stream()
477        .sequential()
478        .filter(e -> e.getKind().isExecutable() && e.getSimpleName().contentEquals(name))
479        .map(UniversalElement.class::cast)
480        .filter(ee -> {
481            if (!this.sameType(returnType, ee.getReturnType())) {
482              return false;
483            }
484            final List<? extends UniversalElement> ps = ee.getParameters();
485            if (ps.size() != parameterTypes.length) {
486              return false;
487            }
488            for (int i = 0; i < parameterTypes.length; i++) {
489              if (!this.sameType(ps.get(i).asType(), parameterTypes[i])) {
490                return false;
491              }
492            }
493            return true;
494          })
495        .findFirst()
496        .orElse(null);
497    }
498    default -> {
499      try (var lock = this.lock()) {
500        final List<? extends Element> ees = declaringElement.getEnclosedElements();
501        yield ees.stream()
502          .sequential()
503          .filter(e -> e.getKind().isExecutable() && e.getSimpleName().contentEquals(name))
504          .map(ExecutableElement.class::cast)
505          .filter(ee -> {
506              if (!this.sameType(returnType, ee.getReturnType())) {
507                return false;
508              }
509              final List<? extends VariableElement> ps = ee.getParameters();
510              if (ps.size() != parameterTypes.length) {
511                return false;
512              }
513              for (int i = 0; i < parameterTypes.length; i++) {
514                if (!this.sameType(ps.get(i).asType(), parameterTypes[i])) {
515                  return false;
516                }
517              }
518              return true;
519            })
520          .findFirst()
521          .orElse(null);
522      }
523    }
524    };
525  }
526
527  /**
528   * A convenience method that returns {@code true} if and only if the supplied {@link Element} is <dfn>generic</dfn>.
529   *
530   * @param e an {@link Element}; must not be {@code null}
531   *
532   * @return {@code true} if and only if the supplied {@link Element} is <dfn>generic</dfn>; {@code false} otherwise
533   *
534   * @exception NullPointerException if {@code e} is {@code null}
535   *
536   * @see Parameterizable
537   *
538   * @spec https://docs.oracle.com/javase/specs/jls/se23/html/jls-8.html#jls-8.1.2 Java Language Specification, section
539   * 8.1.2
540   *
541   * @spec https://docs.oracle.com/javase/specs/jls/se23/html/jls-8.html#jls-8.4.4 Java Language Specification, section
542   * 8.4.4
543   *
544   * @spec https://docs.oracle.com/javase/specs/jls/se23/html/jls-8.html#jls-8.8.4 Java Language Specification, section
545   * 8.8.4
546   *
547   * @spec https://docs.oracle.com/javase/specs/jls/se23/html/jls-9.html#jls-9.1.2 Java Language Specification, section
548   * 9.1.2
549   */
550  public default boolean generic(final Element e) {
551    return switch (e) {
552    case null -> throw new NullPointerException("e");
553    case UniversalElement ue -> ue.generic();
554    case Parameterizable p -> {
555      try (var lock = this.lock()) {
556        yield switch (e.getKind()) {
557        case CLASS, CONSTRUCTOR, ENUM, INTERFACE, METHOD, RECORD -> !p.getTypeParameters().isEmpty();
558        default -> false;
559        };
560      }
561    }
562    default -> false;
563    };
564  }
565
566  /**
567   * A convenience method that returns {@code true} if and only if the supplied {@link TypeMirror} is declared by an
568   * {@link Element} that {@linkplain #generic(Element) is generic}.
569   *
570   * @param t a {@link TypeMirror}; must not be {@code null}
571   *
572   * @return {@code true} if and only if the supplied {@link TypeMirror} is declared by an {@link Element} that
573   * {@linkplain #generic(Element) is generic}
574   *
575   * @exception NullPointerException if {@code t} is {@code null}
576   *
577   * @see #generic(Element)
578   *
579   * @spec https://docs.oracle.com/javase/specs/jls/se23/html/jls-8.html#jls-8.1.2 Java Language Specification, section
580   * 8.1.2
581   *
582   * @spec https://docs.oracle.com/javase/specs/jls/se23/html/jls-8.html#jls-8.4.4 Java Language Specification, section
583   * 8.4.4
584   *
585   * @spec https://docs.oracle.com/javase/specs/jls/se23/html/jls-8.html#jls-8.8.4 Java Language Specification, section
586   * 8.8.4
587   *
588   * @spec https://docs.oracle.com/javase/specs/jls/se23/html/jls-9.html#jls-9.1.2 Java Language Specification, section
589   * 9.1.2
590   */
591  public default boolean generic(final TypeMirror t) {
592    return switch (t) {
593    case null -> throw new NullPointerException("t");
594    case UniversalType ut -> ut.generic();
595    default -> {
596      try (var lock = this.lock()) {
597        final Element e = this.element(t);
598        yield e != null && this.generic(e);
599      }
600    }
601    };
602  }
603
604  /**
605   * A convenience method that returns {@code true} if and only if the supplied {@link Element} represents the (essentially
606   * primordial) {@code java.lang.Object} {@link Element}.
607   *
608   * @param e an {@link Element}; must not be {@code null}
609   *
610   * @return {@code true} if and only if the supplied {@link Element} represents the (essentially
611   * primordial) {@code java.lang.Object} {@link Element}; {@code false} otherwise
612   *
613   * @exception NullPointerException if {@code e} is {@code null}
614   */
615  public default boolean javaLangObject(final Element e) {
616    return switch (e) {
617    case null -> throw new NullPointerException("e");
618    case UniversalElement ue -> ue.javaLangObject();
619    default -> {
620      try (var lock = this.lock()) {
621        yield
622          e.getKind() == ElementKind.CLASS &&
623          ((QualifiedNameable)e).getQualifiedName().contentEquals("java.lang.Object");
624      }
625    }
626    };
627  }
628
629  /**
630   * A convenience method that returns {@code true} if and only if the supplied {@link TypeMirror} represents the {@link
631   * DeclaredType} declared by the (essentially primordial) {@code java.lang.Object} element.
632   *
633   * @param t a {@link TypeMirror}; must not be {@code null}
634   *
635   * @return {@code true} represents the {@link
636   * DeclaredType} declared by the (essentially primordial) {@code java.lang.Object} element; {@code false} otherwise
637   *
638   * @exception NullPointerException if {@code t} is {@code null}
639   *
640   * @see #javaLangObject(Element)
641   */
642  public default boolean javaLangObject(final TypeMirror t) {
643    return switch (t) {
644    case null -> throw new NullPointerException("t");
645    case UniversalType ut -> ut.javaLangObject();
646    default -> {
647      try (var lock = this.lock()) {
648        yield
649          t.getKind() == TypeKind.DECLARED &&
650          javaLangObject(((DeclaredType)t).asElement());
651      }
652    }
653    };
654  }
655
656  /**
657   * A convenience method that returns the {@link TypeElement} representing the class named {@link Object
658   * java.lang.Object}.
659   *
660   * @return a non-{@code null} {@link TypeElement} whose {@linkplain TypeElement#getQualifiedName() qualified name} is
661   * {@linkplain Name#contentEquals(CharSequence) equal to} {@code java.lang.Object}
662   *
663   * @see #typeElement(CharSequence)
664   */
665  public default TypeElement javaLangObject() {
666    return this.typeElement("java.lang.Object");
667  }
668
669  /**
670   * Semantically locks an opaque lock used to serialize symbol completion, and returns it in the form of an {@link
671   * Unlockable}.
672   *
673   * <p>Implementations of this method must not return {@code null}.</p>
674   *
675   * @return an {@link Unlockable} in a semantically locked state; never {@code null}
676   *
677   * @see Unlockable#close()
678   */
679  public Unlockable lock();
680
681  /**
682   * Returns a {@link ModuleElement} representing the module {@linkplain ModuleElement#getQualifiedName() named} by the
683   * supplied {@code qualifiedName}, <strong>or {@code null} if there is no such {@link ModuleElement}</strong>.
684   *
685   * @param qualifiedName a name suitable for naming a module; must not be {@code null}; may be {@linkplain
686   * CharSequence#isEmpty() empty}, in which case a {@link ModuleElement} representing an <dfn>unnamed module</dfn> will
687   * be returned
688   *
689   * @return a {@link ModuleElement}, or {@code null}
690   *
691   * @exception NullPointerException if {@code qualifiedName} is {@code null}
692   *
693   * @see javax.lang.model.util.Elements#getModuleElement(CharSequence)
694   *
695   * @spec https://docs.oracle.com/javase/specs/jls/se23/html/jls-7.html#jls-7.7 Java Language Specification, section
696   * 7.7
697   */
698  public ModuleElement moduleElement(final CharSequence qualifiedName);
699
700  /**
701   * Returns a {@link Name} representing the supplied {@link CharSequence}.
702   *
703   * @param name a {@link CharSequence}; must not be {@code null}
704   *
705   * @return a non-{@code null} {@link Name} representing the supplied {@link name}
706   *
707   * @exception NullPointerException if {@code name} is {@code null}
708   *
709   * @see #lock()
710   *
711   * @see javax.lang.model.util.Elements#getName(CharSequence)
712   */
713  public Name name(final CharSequence name);
714
715  /**
716   * Returns a {@link NoType} {@linkplain TypeMirror#getKind() bearing} the supplied {@link TypeKind}, if the supplied
717   * {@link TypeKind} is either {@link TypeKind#NONE} or {@link TypeKind#VOID}.
718   *
719   * @param kind a {@link TypeKind}; must be either {@link TypeKind#NONE} or {@link TypeKind#VOID}
720   *
721   * @return a non-{@code null} {@link NoType} {@linkplain TypeMirror#getKind() bearing} the supplied {@link TypeKind}
722   *
723   * @exception NullPointerException if {@code kind} is {@code null}
724   *
725   * @exception IllegalArgumentException if {@code kind} is non-{@code null} and neither {@link TypeKind#NONE} nor
726   * {@link TypeKind#VOID}
727   *
728   * @see TypeKind#NONE
729   *
730   * @see TypeKind#VOID
731   *
732   * @see javax.lang.model.util.Types#getNoType(TypeKind)
733   *
734   * @spec https://docs.oracle.com/javase/specs/jls/se23/html/jls-8.html#jls-8.4.5 Java Language Specification, section
735   * 8.4.5
736   */
737  public NoType noType(final TypeKind kind);
738
739  /**
740   * Returns a {@link NullType} implementation {@linkplain TypeMirror#getKind() whose <code>TypeKind</code>} is {@link
741   * TypeKind#NULL}.
742   *
743   * @return a non-{@code null} {@link NullType}
744   *
745   * @see javax.lang.model.util.Types#getNullType()
746   *
747   * @spec https://docs.oracle.com/javase/specs/jls/se23/html/jls-4.html#jls-4.1 Java Language Specification, section 4.1
748   */
749  public NullType nullType();
750
751  /**
752   * Returns the {@linkplain Origin origin} of the supplied {@link Element}.
753   *
754   * @param e a non-{@code null} {@link Element}
755   *
756   * @return a non-{@code null} {@link Origin}
757   *
758   * @see Elements#getOrigin(Element)
759   */
760  public Origin origin(final Element e);
761
762  /**
763   * Returns a {@link PackageElement} representing the package bearing the supplied {@code canonicalName}, <strong>or
764   * {@code null} if there is no such {@link PackageElement}</strong>.
765   *
766   * @param canonicalName a canonical name suitable for naming a package; must not be {@code null}; may be {@linkplain
767   * CharSequence#isEmpty() empty}, in which case a {@link ModuleElement} representing an <dfn>unnamed package</dfn> will
768   * be returned
769   *
770   * @return a {@link PackageElement}, or {@code null}
771   *
772   * @exception NullPointerException if {@code canonicalName} is {@code null}
773   *
774   * @see javax.lang.model.util.Elements#getPackageElement(CharSequence)
775   *
776   * @spec https://docs.oracle.com/javase/specs/jls/se23/html/jls-6.html#jls-6.7 Java Language Specification, section
777   * 6.7
778   */
779  public PackageElement packageElement(final CharSequence canonicalName);
780
781  /**
782   * Returns a {@link PackageElement} representing the package bearing the supplied {@code canonicalName} as seen from
783   * the module represented by the supplied {@link ModuleElement}, <strong>or {@code null} if there is no such {@link
784   * PackageElement}</strong>.
785   *
786   * @param asSeenFrom a {@link ModuleElement}; must not be {@code null}
787   *
788   * @param canonicalName a canonical name suitable for naming a package; must not be {@code null}; may be {@linkplain
789   * CharSequence#isEmpty() empty}, in which case a {@link ModuleElement} representing an <dfn>unnamed package</dfn> will
790   * be returned
791   *
792   * @return a {@link PackageElement}, or {@code null}
793   *
794   * @exception NullPointerException if either {@code asSeenFrom} or {@code canonicalName} is {@code null}
795   *
796   * @see javax.lang.model.util.Elements#getPackageElement(ModuleElement, CharSequence)
797   *
798   * @spec https://docs.oracle.com/javase/specs/jls/se23/html/jls-6.html#jls-6.7 Java Language Specification, section
799   * 6.7
800   */
801  public PackageElement packageElement(final ModuleElement asSeenFrom, final CharSequence canonicalName);
802
803  /**
804   * Returns a {@link Parameterizable} corresponding to the supplied (reflective) {@link GenericDeclaration}.
805   *
806   * @param gd a {@link GenericDeclaration}; must not be {@code null}
807   *
808   * @return a {@link Parameterizable} corresponding to the supplied {@link GenericDeclaration}; never {@code null}
809   *
810   * @exception NullPointerException if {@code gd} is {@code null}
811   *
812   * @exception IllegalArgumentException if {@code gd} is neither a {@link Class} nor an {@link Executable}
813   */
814  public default Parameterizable parameterizable(final GenericDeclaration gd) {
815    return switch (gd) {
816    case null -> throw new NullPointerException("gd");
817    case Class<?> c -> this.typeElement(c.getCanonicalName());
818    case Executable e -> this.executableElement(e);
819    default -> throw new IllegalArgumentException("gd: " + gd);
820    };
821  }
822
823  /**
824   * A convenience method that returns {@code true} if and only if {@code t} is a {@link DeclaredType}, {@linkplain
825   * TypeMirror#getKind() has a <code>TypeKind</code>} of {@link TypeKind#DECLARED DECLARED}, and {@linkplain
826   * DeclaredType#getTypeArguments() has an empty type arguments list}.
827   *
828   * @param t a {@link TypeMirror}; must not be {@code null}
829   *
830   * @return {@code true} if and only if {@code t} is a {@link DeclaredType}, {@linkplain
831   * TypeMirror#getKind() has a <code>TypeKind</code>} of {@link TypeKind#DECLARED DECLARED}, and {@linkplain
832   * DeclaredType#getTypeArguments() has an empty type arguments list}; {@code false} otherwise
833   *
834   * @exception NullPointerException if {@code t} is {@code null}
835   */
836  public default boolean parameterized(final TypeMirror t) {
837    return switch (t) {
838    case null -> throw new NullPointerException("t");
839    case UniversalType ut -> ut.parameterized();
840    default -> {
841      try (var lock = this.lock()) {
842        yield t.getKind() == TypeKind.DECLARED && !((DeclaredType)t).getTypeArguments().isEmpty();
843      }
844    }
845    };
846  }
847
848  /**
849   * Returns the result of applying <dfn>unboxing conversion</dfn> to the (logical) {@link TypeElement} bearing the
850   * supplied {@code canonicalName}.
851   *
852   * @param canonicalName a canonical name of either a primitive type or a so-called "wrapper" type; must not be {@code
853   * null}
854   *
855   * @return a non-{@code null} {@link PrimitiveType} with a {@linkplain TypeKind#isPrimitive() primitive} {@link
856   * TypeKind}
857   *
858   * @exception NullPointerException if {@code canonicalName} is {@code null}
859   *
860   * @exception IllegalArgumentException if {@code canonicalName} {@linkplain #toString(CharSequence) converted to a
861   * <code>String</code>} {@linkplain String#equals(Object) is not equal to} {@code boolean}, {@code byte}, {@code
862   * char}, {@code double}, {@code float}, {@code int}, {@code long}, {@code short}, {@link Boolean java.lang.Boolean},
863   * {@link Byte java.lang.Byte}, {@link Character java.lang.Character}, {@link Double java.lang.Double}, {@link Float
864   * java.lang.Float}, {@link Integer java.lang.Integer}, {@link Long java.lang.Long}, or {@link Short java.lang.Short}
865   *
866   * @see #primitiveType(TypeKind)
867   *
868   * @spec https://docs.oracle.com/javase/specs/jls/se23/html/jls-5.html#jls-5.1.8 Java Language Specification, section
869   * 5.1.8
870   *
871   * @spec https://docs.oracle.com/javase/specs/jls/se23/html/jls-6.html#jls-6.7 Java Language Specification, section
872   * 6.7
873   */
874  // (Convenience.)
875  // (Unboxing.)
876  public default PrimitiveType primitiveType(final CharSequence canonicalName) {
877    final String s = this.toString(Objects.requireNonNull(canonicalName, "canonicalName"));
878    return switch (s) {
879    case "boolean", "java.lang.Boolean" -> this.primitiveType(TypeKind.BOOLEAN);
880    case "byte", "java.lang.Byte" -> this.primitiveType(TypeKind.BYTE);
881    case "char", "java.lang.Character" -> this.primitiveType(TypeKind.CHAR);
882    case "double", "java.lang.Double" -> this.primitiveType(TypeKind.DOUBLE);
883    case "float", "java.lang.Float" -> this.primitiveType(TypeKind.FLOAT);
884    case "int", "java.lang.Integer" -> this.primitiveType(TypeKind.INT);
885    case "long", "java.lang.Long" -> this.primitiveType(TypeKind.LONG);
886    case "short", "java.lang.Short" -> this.primitiveType(TypeKind.SHORT);
887    default -> throw new IllegalArgumentException("canonicalName: " + s);
888    };
889  }
890
891  /**
892   * Returns the result of applying <dfn>unboxing conversion</dfn> to the {@linkplain Element#asType() type declared by
893   * the supplied <code>TypeElement</code>}.
894   *
895   * @param e a {@link TypeElement}; must not be {@code null}
896   *
897   * @return a non-{@code null} {@link PrimitiveType} with a {@linkplain TypeKind#isPrimitive() primitive} {@link
898   * TypeKind}
899   *
900   * @exception NullPointerException if {@code e} is {@code null}
901   *
902   * @exception IllegalArgumentException if there is no unboxing conversion that can be applied to the {@linkplain
903   * Element#asType() type declared by <code>e</code>}
904   *
905   * @see javax.lang.model.util.Types#unboxedType(TypeMirror)
906   *
907   * @see #primitiveType(TypeMirror)
908   *
909   * @spec https://docs.oracle.com/javase/specs/jls/se23/html/jls-5.html#jls-5.1.8 Java Language Specification, section
910   * 5.1.8
911   */
912  // (Convenience.)
913  // (Unboxing.)
914  public default PrimitiveType primitiveType(final TypeElement e) {
915    return this.primitiveType(e.asType());
916  }
917
918  /**
919   * Returns the {@link PrimitiveType} corresponding to the supplied {@link TypeKind} (if it {@linkplain
920   * TypeKind#isPrimitive() is primitive}).
921   *
922   * @param kind a {@linkplain TypeKind#isPrimitive() primitive} {@link TypeKind}; must not be {@code null}
923   *
924   * @return a non-{@code null} {@link PrimitiveType} {@linkplain TypeMirror#getKind() with} a {@linkplain
925   * TypeKind#isPrimitive() primitive} {@link TypeKind}
926   *
927   * @exception NullPointerException if {@code kind} is {@code null}
928   *
929   * @exception IllegalArgumentException if {@code kind} {@linkplain TypeKind#isPrimitive() is not primitive}
930   *
931   * @see javax.lang.model.util.Types#getPrimitiveType(TypeKind)
932   *
933   * @spec https://docs.oracle.com/javase/specs/jls/se23/html/jls-4.html#jls-4.2 Java Language Specification, section
934   * 4.2
935   */
936  // (Canonical.)
937  public PrimitiveType primitiveType(final TypeKind kind);
938
939  /**
940   * Returns the result of applying <dfn>unboxing conversion</dfn> to the supplied {@link TypeMirror}.
941   *
942   * @param t a {@link TypeMirror}; must not be {@code null}
943   *
944   * @return a non-{@code null} {@link PrimitiveType} with a {@linkplain TypeKind#isPrimitive() primitive} {@link
945   * TypeKind}
946   *
947   * @exception NullPointerException if {@code t} is {@code null}
948   *
949   * @exception IllegalArgumentException if there is no unboxing conversion that can be applied to {@code t}
950   *
951   * @see javax.lang.model.util.Types#unboxedType(TypeMirror)
952   *
953   * @spec https://docs.oracle.com/javase/specs/jls/se23/html/jls-5.html#jls-5.1.8 Java Language Specification, section
954   * 5.1.8
955   */
956  // (Canonical.)
957  // (Unboxing.)
958  public PrimitiveType primitiveType(final TypeMirror t);
959
960  /**
961   * A convenience method that returns {@code true} if and only if the supplied {@link TypeMirror} is a <dfn>raw
962   * type</dfn> according to <a href="https://docs.oracle.com/javase/specs/jls/se23/html/jls-4.html#jls-4.8">the rules
963   * of the Java Language Specification</a>
964   *
965   * @param t a {@link TypeMirror}; must not be {@code null}
966   *
967   * @return {@code true} if and only if the supplied {@link TypeMirror} is a <dfn>raw type</dfn> according to <a
968   * href="https://docs.oracle.com/javase/specs/jls/se23/html/jls-4.html#jls-4.8">the rules of the Java Language
969   * Specification</a>
970   *
971   * @exception NullPointerException if {@code t} is {@code null}
972   *
973   * @spec https://docs.oracle.com/javase/specs/jls/se23/html/jls-4.html#jls-4.8 Java Language Specification, section 4.8
974   */
975  public default boolean raw(final TypeMirror t) {
976    return switch (t) {
977    case null -> throw new NullPointerException("t");
978    case UniversalType ut -> ut.raw();
979    default -> {
980      try (var lock = this.lock()) {
981        yield switch (t.getKind()) {
982        case ARRAY -> raw(elementType((ArrayType)t));
983        case DECLARED -> {
984          final DeclaredType dt = (DeclaredType)t;
985          yield generic(dt.asElement()) && dt.getTypeArguments().isEmpty();
986        }
987        default -> false;
988        };
989      }
990    }
991    };
992  }
993
994  /**
995   * A convenience method that returns the <dfn>raw type</dfn> corresponding to {@code t}, <strong>or {@code null} if
996   * {@code t} is <a href="https://docs.oracle.com/javase/specs/jls/se23/html/jls-4.html#jls-4.8">incapable of yielding
997   * a raw type</a></strong>.
998   *
999   * <p>Overrides of this method must conform to the requirements imposed by the relevant section of the relevant
1000   * version of the Java Language Specification concerning raw types.</p>
1001   *
1002   * @param t a {@link TypeMirror}; must not be {@code null}
1003   *
1004   * @return the raw type corresponding to the supplied {@link TypeMirror}, or {@code null} if {@code t} is <a
1005   * href="https://docs.oracle.com/javase/specs/jls/se23/html/jls-4.html#jls-4.8">incapable of yielding a raw type</a>
1006   *
1007   * @exception NullPointerException if {@code t} is {@code null}
1008   *
1009   * @spec https://docs.oracle.com/javase/specs/jls/se23/html/jls-4.html#jls-4.8 Java Language Specification, section
1010   * 4.8
1011   */
1012  public default TypeMirror rawType(final TypeMirror t) {
1013    return switch (t) {
1014    case null -> throw new NullPointerException("t");
1015    case UniversalType ut -> ut.rawType();
1016    default -> {
1017      try (var lock = this.lock()) {
1018        yield switch (t.getKind()) {
1019        case ARRAY -> this.rawType(this.elementType(t)); // recursive
1020        default -> this.parameterized(t) ? this.erasure(t) : null;
1021        };
1022      }
1023    }
1024    };
1025  }
1026
1027  /**
1028   * Returns a {@link RecordComponentElement} corresponding to the supplied {@link ExecutableElement}, <strong>or {@code
1029   * null} if there is no such {@link RecordComponentElement}</strong>.
1030   *
1031   * @param e an {@link ExecutableElement} {@linkplain ExecutableElement#getEnclosingElement() enclosed by} a record
1032   * representing an <dfn>accessor method</dfn>; must not be {@code null}
1033   *
1034   * @return a {@link RecordComponentElement} corresponding to the supplied {@link ExecutableElement}, or {@code null}
1035   *
1036   * @exception NullPointerException if {@code e} is {@code null}
1037   *
1038   * @spec https://docs.oracle.com/javase/specs/jls/se23/html/jls-8.html#jls-8.10.3 Java Language Specification, section
1039   * 8.10.3
1040   */
1041  public RecordComponentElement recordComponentElement(final ExecutableElement e);
1042
1043  /**
1044   * Returns {@code true} if and only if the two arguments represent the <dfn>same type</dfn>.
1045   *
1046   * <p>This method differs from the {@link javax.lang.model.util.Types#isSameType(TypeMirror, TypeMirror)} method in
1047   * two ways:</p>
1048   *
1049   * <ul>
1050   *
1051   * <li>Its arguments may be {@code null}. If both arguments are {@code null}, {@code true} is returned. If only one
1052   * argument is {@code null}, {@code false} is returned.</li>
1053   *
1054   * <li>If the same Java object reference is passed as both arguments, {@code true} is returned (even if it {@linkplain
1055   * TypeMirror#getKind() has a <code>TypeKind</code>} of {@link TypeKind#WILDCARD}).</li>
1056   *
1057   * </ul>
1058   *
1059   * @param t0 the first {@link TypeMirror}; may be {@code null}
1060   *
1061   * @param t1 the second {@link TypeMirror}; may be {@code null}
1062   *
1063   * @return {@code true} if and only if the two arguments represent the <dfn>same type</dfn>; {@code false} otherwise
1064   *
1065   * @see javax.lang.model.util.Types#isSameType(TypeMirror, TypeMirror)
1066   *
1067   * @spec https://docs.oracle.com/javase/specs/jls/se23/html/jls-4.html#jls-4.3.4 Java Language Specification, section
1068   * 4.3.4
1069   *
1070   * @spec https://docs.oracle.com/javase/specs/jls/se23/html/jls-4.html#jls-4.5.1 Java Language Specification, section
1071   * 4.5.1
1072   */
1073  public boolean sameType(final TypeMirror t0, final TypeMirror t1);
1074
1075  /**
1076   * Returns {@code true} if and only if {@code et0} is a <dfn>subsignature</dfn> of {@code et1}.
1077   *
1078   * @param et0 the first {@link ExecutableType}; must not be {@code null}
1079   *
1080   * @param et1 the second {@link ExecutableType}; must not be {@code null}
1081   *
1082   * @return {@code true} if and only if {@code et0} is a <dfn>subsignature</dfn> of {@code et1}
1083   *
1084   * @exception NullPointerException if either argument is {@code null}
1085   *
1086   * @exception ClassCastException if this method is implemented in terms of the {@link
1087   * javax.lang.model.util.Types#isSubsignature(ExecutableType, ExecutableType)} method, and if each of the supplied
1088   * {@link ExecutableType} arguments does not have an {@linkplain TypeKind#EXECUTABLE <code>EXECUTABLE</code>
1089   * <code>TypeKind</code>}; this exception type is undocumented by the {@link
1090   * javax.lang.model.util.Types#isSubsignature(ExecutableType, ExecutableType)} method and so is subject to change
1091   * without prior notice
1092   *
1093   * @see javax.lang.model.util.Types#isSubsignature(ExecutableType, ExecutableType)
1094   *
1095   * @spec https://docs.oracle.com/javase/specs/jls/se23/html/jls-8.html#jls-8.4.2 Java Language Specification, section
1096   * 8.4.2
1097   */
1098  public boolean subsignature(final ExecutableType et0, final ExecutableType et1);
1099
1100  /**
1101   * Returns {@code true} if and only if {@code candidateSubtype} is a <dfn>subtype</dfn> of {@code supertype}.
1102   *
1103   * @param candidateSubtype the first {@link TypeMirror}; must not be {@code null}
1104   *
1105   * @param supertype the second {@link TypeMirror}; must not be {@code null}
1106   *
1107   * @return {@code true} if and only if {@code candidateSubtype} is a <dfn>subtype</dfn> of {@code supertype}
1108   *
1109   * @exception NullPointerException if either argument is {@code null}
1110   *
1111   * @see javax.lang.model.util.Types#isSubtype(TypeMirror, TypeMirror)
1112   *
1113   * @spec https://docs.oracle.com/javase/specs/jls/se23/html/jls-4.html#jls-4.10 Java Language Specification, section
1114   * 4.10
1115   */
1116  public boolean subtype(TypeMirror candidateSubtype, TypeMirror supertype);
1117
1118  /**
1119   * Converts the supplied {@link CharSequence}, which is often a {@link Name}, into a {@link String}, and returns the
1120   * conversion, {@linkplain #lock() locking} when appropriate to serialize symbol completion.
1121   *
1122   * @param name the {@link CharSequence} to convert; may be {@code null} in which case {@code null} will be returned
1123   *
1124   * @return a {@link String}, or {@code null} if {@code name} was {@code null}
1125   *
1126   * @see #lock()
1127   */
1128  public default String toString(final CharSequence name) {
1129    return switch (name) {
1130    case null -> null;
1131    case String s -> s;
1132    case StringName sn -> sn.value();
1133    case Name n -> {
1134      try (var lock = this.lock()) {
1135        yield n.toString();
1136      }
1137    }
1138    default -> name.toString();
1139    };
1140  }
1141
1142  /**
1143   * A convenience method that returns the {@link TypeMirror} corresponding to the supplied (reflective) {@link Type}.
1144   *
1145   * @param t a {@link Type}; must not be {@code null}
1146   *
1147   * @return the {@link TypeMirror} corresponding to the supplied {@link Type}; never {@code null}
1148   *
1149   * @exception NullPointerException if {@code t} is {@code null}
1150   *
1151   * @exception IllegalArgumentException if {@code t} is not a {@link Class}, {@link GenericArrayType}, {@link
1152   * ParameterizedType}, {@link java.lang.reflect.TypeVariable} or {@link java.lang.reflect.WildcardType}
1153   */
1154  public default TypeMirror type(final Type t) {
1155    // TODO: anywhere there is domain.declaredType(), consider passing
1156    // domain.moduleElement(this.getClass().getModule().getName()) as the first argument. Not sure how this works
1157    // exactly but I think it might be necessary.
1158    return switch (t) {
1159    case null -> throw new NullPointerException("t");
1160    case Class<?> c when t == boolean.class -> this.primitiveType(TypeKind.BOOLEAN);
1161    case Class<?> c when t == byte.class -> this.primitiveType(TypeKind.BYTE);
1162    case Class<?> c when t == char.class -> this.primitiveType(TypeKind.CHAR);
1163    case Class<?> c when t == double.class -> this.primitiveType(TypeKind.DOUBLE);
1164    case Class<?> c when t == float.class -> this.primitiveType(TypeKind.FLOAT);
1165    case Class<?> c when t == int.class -> this.primitiveType(TypeKind.INT);
1166    case Class<?> c when t == long.class -> this.primitiveType(TypeKind.LONG);
1167    case Class<?> c when t == short.class -> this.primitiveType(TypeKind.SHORT);
1168    case Class<?> c when t == void.class -> this.noType(TypeKind.VOID);
1169    case Class<?> c when t == Object.class -> this.javaLangObject().asType(); // cheap and easy optimization
1170    case Class<?> c when c.isArray() -> this.arrayTypeOf(this.type(c.getComponentType()));
1171    case Class<?> c -> this.declaredType(c.getCanonicalName());
1172    case GenericArrayType g -> this.arrayTypeOf(this.type(g.getGenericComponentType()));
1173    case ParameterizedType pt when pt.getOwnerType() == null ->
1174      this.declaredType(this.typeElement(((Class<?>)pt.getRawType()).getCanonicalName()),
1175                        this.types(pt.getActualTypeArguments()));
1176    case ParameterizedType pt ->
1177      this.declaredType((DeclaredType)this.type(pt.getOwnerType()),
1178                        this.typeElement(((Class<?>)pt.getRawType()).getCanonicalName()),
1179                        this.types(pt.getActualTypeArguments()));
1180    case java.lang.reflect.TypeVariable<?> tv -> this.typeVariable(this.parameterizable(tv.getGenericDeclaration()), tv.getName());
1181    case java.lang.reflect.WildcardType w when w.getLowerBounds().length <= 0 -> this.wildcardType(this.type(w.getUpperBounds()[0]), null);
1182    case java.lang.reflect.WildcardType w -> this.wildcardType(null, this.type(w.getLowerBounds()[0]));
1183    default -> throw new IllegalArgumentException("t: " + t);
1184    };
1185  }
1186
1187  /**
1188   * A convenience method that returns an array of {@link TypeMirror}s whose elements correspond, in order, to the
1189   * elements in the supplied {@link Type} array.
1190   *
1191   * @param ts an array of {@link Type}s; must not be {@code null}
1192   *
1193   * @return an array of {@link TypeMirror}s whose elements correspond, in order, to the elements in the supplied {@link
1194   * Type} array; never {@code null}
1195   *
1196   * @exception NullPointerException if {@code ts} is {@code null} or contains {@code null} elements
1197   *
1198   * @exception IllegalArgumentException if any element of {@code ts} is deemed illegal by the {@link #type(Type)}
1199   * method
1200   *
1201   * @see #type(Type)
1202   */
1203  public default TypeMirror[] types(final Type[] ts) {
1204    return switch (ts.length) {
1205    case 0 -> new TypeMirror[0];
1206    case 1 -> new TypeMirror[] { this.type(ts[0]) };
1207    default -> {
1208      final TypeMirror[] rv = new TypeMirror[ts.length];
1209      for (int i = 0; i < ts.length; i++) {
1210        rv[i] = this.type(ts[i]);
1211      }
1212      yield rv;
1213    }
1214    };
1215  }
1216
1217  /**
1218   * Returns a {@link TypeElement} representing the element bearing the supplied <dfn>canonical name</dfn>, <strong>or
1219   * {@code null} if there is no such {@link TypeElement}</strong>.
1220   *
1221   * @param canonicalName a valid canonical name; must not be {@code null}
1222   *
1223   * @return a {@link TypeElement}, or {@code null}
1224   *
1225   * @exception NullPointerException if {@code canonicalName} is {@code null}
1226   *
1227   * @see javax.lang.model.util.Elements#getTypeElement(CharSequence)
1228   *
1229   * @spec https://docs.oracle.com/javase/specs/jls/se23/html/jls-6.html#jls-6.7 Java Language Specification, section
1230   * 6.7
1231   */
1232  public TypeElement typeElement(final CharSequence canonicalName);
1233
1234  /**
1235   * Returns a {@link TypeElement} representing the element bearing the supplied <dfn>canonical name</dfn>, as read or
1236   * seen from the module represented by the supplied {@link ModuleElement}, <strong>or {@code null} if there is no such
1237   * {@link TypeElement}</strong>.
1238   *
1239   * @param asSeenFrom a {@link ModuleElement}; must not be {@code null}
1240   *
1241   * @param canonicalName a valid canonical name; must not be {@code null}
1242   *
1243   * @return a {@link TypeElement}, or {@code null}
1244   *
1245   * @exception NullPointerException if either {@code asSeenFrom} or {@code canonicalName} is {@code null}
1246   *
1247   * @see javax.lang.model.util.Elements#getTypeElement(ModuleElement, CharSequence)
1248   *
1249   * @spec https://docs.oracle.com/javase/specs/jls/se23/html/jls-6.html#jls-6.7 Java Language Specification, section
1250   * 6.7
1251   */
1252  public TypeElement typeElement(final ModuleElement asSeenFrom, final CharSequence canonicalName);
1253
1254  /**
1255   * Returns a {@link TypeElement} representing the result of applying <dfn>boxing conversion</dfn> to the primitive
1256   * type represented by the supplied {@link PrimitiveType} argument.
1257   *
1258   * <p>The default implementation of this method calls the {@link #typeElement(TypeKind)} method with the supplied
1259   * {@link PrimitiveType}'s {@linkplain TypeMirror#getKind() affiliated <code>TypeKind</code>} and returns its
1260   * result.</p>
1261   *
1262   * @param t a {@link PrimitiveType} with a {@link TypeKind} that {@linkplain TypeKind#isPrimitive() is primitive};
1263   * must not be {@code null}
1264   *
1265   * @return a non-{@code null} {@link TypeElement} representing the result of applying boxing conversion to the
1266   * supplied argument
1267   *
1268   * @exception NullPointerException if {@code t} is {@code null}
1269   *
1270   * @exception ClassCastException if this method is implemented in terms of the {@link
1271   * javax.lang.model.util.Types#boxedClass(PrimitiveType)} method, and if the supplied {@link PrimitiveType} does not
1272   * have a {@linkplain TypeKind#isPrimitive() primitive <code>TypeKind</code>}; this exception type is undocumented by
1273   * the {@link javax.lang.model.util.Types#boxedClass(PrimitiveType)} method and so is subject to change without prior
1274   * notice
1275   *
1276   * @exception IllegalArgumentException if {@code primitiveTypeKind} {@linkplain TypeKind#isPrimitive() is not
1277   * primitive}
1278   *
1279   * @see #typeElement(TypeKind)
1280   *
1281   * @see javax.lang.model.util.Types#boxedClass(PrimitiveType)
1282   *
1283   * @spec https://docs.oracle.com/javase/specs/jls/se23/html/jls-5.html#jls-5.1.7 Java Language Specification, section
1284   * 5.1.7
1285   */
1286  // (Canonical.)
1287  // (Boxing.)
1288  public default TypeElement typeElement(final PrimitiveType t) {
1289    return switch (t) {
1290    case null -> throw new NullPointerException("t");
1291    case UniversalType ut -> this.typeElement(ut.getKind());
1292    default -> {
1293      try (var lock = this.lock()) {
1294        yield this.typeElement(t.getKind());
1295      }
1296    }
1297    };
1298  }
1299
1300  /**
1301   * Returns a {@link TypeElement} representing the result of applying <dfn>boxing conversion</dfn> to the primitive
1302   * type represented by the supplied {@link TypeKind} argument, if it {@linkplain TypeKind#isPrimitive() is primitive}.
1303   *
1304   * @param primitiveTypeKind a {@link TypeKind} that {@linkplain TypeKind#isPrimitive() is primitive}; must not be
1305   * {@code null}
1306   *
1307   * @return a non-{@code null} {@link TypeElement} representing the result of applying boxing conversion to the
1308   * supplied argument
1309   *
1310   * @exception IllegalArgumentException if {@code primitiveTypeKind} {@linkplain TypeKind#isPrimitive() is not
1311   * primitive}
1312   *
1313   * @see #typeElement(PrimitiveType)
1314   *
1315   * @see javax.lang.model.util.Types#boxedClass(PrimitiveType)
1316   *
1317   * @spec https://docs.oracle.com/javase/specs/jls/se23/html/jls-5.html#jls-5.1.7 Java Language Specification, section
1318   * 5.1.7
1319   */
1320  // (Convenience.)
1321  // (Boxing.)
1322  public default TypeElement typeElement(final TypeKind primitiveTypeKind) {
1323    return switch (primitiveTypeKind) {
1324    case BOOLEAN -> this.typeElement("java.lang.Boolean");
1325    case BYTE -> this.typeElement("java.lang.Byte");
1326    case CHAR -> this.typeElement("java.lang.Character");
1327    case DOUBLE -> this.typeElement("java.lang.Double");
1328    case FLOAT -> this.typeElement("java.lang.Float");
1329    case INT -> this.typeElement("java.lang.Integer");
1330    case LONG -> this.typeElement("java.lang.Long");
1331    case SHORT -> this.typeElement("java.lang.Short");
1332    default -> throw new IllegalArgumentException("primitiveTypeKind: " + primitiveTypeKind);
1333    };
1334  }
1335
1336  /**
1337   * Returns the {@link TypeParameterElement} {@linkplain Parameterizable#getTypeParameters() contained} by the supplied
1338   * {@link Parameterizable} whose {@linkplain TypeParameterElement#getSimpleName() name} {@linkplain
1339   * Name#contentEquals(CharSequence) is equal to} the supplied {@code name}, <strong>or {@code null} if there is no
1340   * such {@link TypeParameterElement}</strong>.
1341   *
1342   * @param p a {@link Parameterizable}; must not be {@code null}
1343   *
1344   * @param name a name valid for a type parameter; must not be {@code null}
1345   *
1346   * @return a {@link TypeParameterElement}, or {@code null}
1347   */
1348  public default TypeParameterElement typeParameterElement(Parameterizable p, final CharSequence name) {
1349    Objects.requireNonNull(p, "p");
1350    Objects.requireNonNull(name, "name");
1351    while (p != null) {
1352      switch (p) {
1353      case UniversalElement ue:
1354        for (final UniversalElement tpe : ue.getTypeParameters()) {
1355          if (tpe.getSimpleName().contentEquals(name)) {
1356            return tpe;
1357          }
1358        }
1359        p = ue.getEnclosingElement();
1360        break;
1361      default:
1362        try (var lock = this.lock()) {
1363          for (final TypeParameterElement tpe : p.getTypeParameters()) {
1364            if (tpe.getSimpleName().contentEquals(name)) {
1365              return tpe;
1366            }
1367          }
1368          p = (Parameterizable)((Element)p).getEnclosingElement();
1369        }
1370        break;
1371      }
1372    }
1373    return null;
1374  }
1375
1376  /**
1377   * A convenience method that returns the {@link TypeVariable} {@linkplain TypeParameterElement#asType() declared by}
1378   * the {@link TypeParameterElement} {@linkplain Parameterizable#getTypeParameters() contained} by the supplied {@link
1379   * Parameterizable} whose {@linkplain TypeParameterElement#getSimpleName() name} {@linkplain
1380   * Name#contentEquals(CharSequence) is equal to} the supplied {@code name}, <strong>or {@code null} if there is no
1381   * such {@link TypeParameterElement} or {@link TypeVariable}</strong>.
1382   *
1383   * @param p a {@link Parameterizable}; must not be {@code null}
1384   *
1385   * @param name a name valid for a type parameter; must not be {@code null}
1386   *
1387   * @return a {@link TypeVariable}, or {@code null}
1388   *
1389   * @see #typeParameterElement(Parameterizable, CharSequence)
1390   */
1391  public default TypeVariable typeVariable(Parameterizable p, final CharSequence name) {
1392    final TypeParameterElement e = this.typeParameterElement(p, name);
1393    return e == null ? null : (TypeVariable)e.asType();
1394  }
1395
1396  /**
1397   * A convenience method that returns the first {@link VariableElement} with a {@linkplain ElementKind#isVariable()
1398   * variable <code>ElementKind</code>} and {@linkplain Element#getSimpleName() bearing} the supplied {@code simpleName}
1399   * that the supplied {@code enclosingElement} {@linkplain Element#getEnclosedElements() encloses}, <strong>or {@code
1400   * null} if there is no such {@link VariableElement}</strong>.
1401   *
1402   * @param enclosingElement an {@link Element}; must not be {@code null}
1403   *
1404   * @param simpleName a {@link CharSequence}; must not be {@code null}
1405   *
1406   * @return a {@link VariableElement}, or {@code null}
1407   *
1408   * @exception NullPointerException if either argument is {@code null}
1409   *
1410   * @see Element#getEnclosedElements()
1411   *
1412   * @see ElementKind#isVariable()
1413   *
1414   * @see Element#getSimpleName()
1415   *
1416   * @see VariableElement
1417   */
1418  public default VariableElement variableElement(final Element enclosingElement, final CharSequence simpleName) {
1419    Objects.requireNonNull(simpleName, "simpleName");
1420    return switch (enclosingElement) {
1421    case null -> throw new NullPointerException("enclosingElement");
1422    case UniversalElement ue -> {
1423      for (final UniversalElement ee : ue.getEnclosedElements()) {
1424        if (ee.getKind().isVariable() && ee.getSimpleName().contentEquals(simpleName)) {
1425          yield ee;
1426        }
1427      }
1428      yield null;
1429    }
1430    default -> {
1431      try (var lock = lock()) {
1432        for (final Element ee : enclosingElement.getEnclosedElements()) {
1433          if (ee.getKind().isVariable() && ee.getSimpleName().contentEquals(simpleName)) {
1434            yield (VariableElement)ee;
1435          }
1436        }
1437      }
1438      yield null;
1439    }
1440    };
1441  }
1442
1443  /**
1444   * A convenience method that returns a new {@link WildcardType} {@linkplain TypeMirror#getKind() with a
1445   * <code>TypeKind</code>} of {@link TypeKind#WILDCARD}, an {@linkplain WildcardType#getExtendsBound() extends bound}
1446   * of {@code null}, and a {@linkplain WildcardType#getSuperBound() super bound} of {@code null}.
1447   *
1448   * @return a new, non-{@code null} {@link WildcardType}
1449   *
1450   * @see #wildcardType(TypeMirror, TypeMirror)
1451   *
1452   * @see javax.lang.model.util.Types#getWildcardType(TypeMirror, TypeMirror)
1453   *
1454   * @spec https://docs.oracle.com/javase/specs/jls/se23/html/jls-4.html#jls-4.5.1 Java Language Specification, section
1455   * 4.5.1
1456   */
1457  public default WildcardType wildcardType() {
1458    return this.wildcardType(null, null);
1459  }
1460
1461  /**
1462   * Returns a new {@link WildcardType} {@linkplain TypeMirror#getKind() with a <code>TypeKind</code>} of {@link
1463   * TypeKind#WILDCARD}, an {@linkplain WildcardType#getExtendsBound() extends bound} of the supplied {@code
1464   * extendsBound}, and a {@linkplain WildcardType#getSuperBound() super bound} of the supplied {@code superBound}.
1465   *
1466   * <p>Any argument may be {@code null}. Both arguments may not be non-{@code null}.</p>
1467   *
1468   * @param extendsBound the upper bound of the new {@link WildcardType}; may be {@code null}
1469   *
1470   * @param superBound the lower bound of the new {@link WildcardType}; may be {@code null}
1471   *
1472   * @return a new, non-{@code null} {@link WildcardType}
1473   *
1474   * @exception IllegalArgumentException if both arguments are non-{@code null} or otherwise unsuitable for being the
1475   * bounds of a {@link WildcardType}
1476   *
1477   * @see javax.lang.model.util.Types#getWildcardType(TypeMirror, TypeMirror)
1478   *
1479   * @spec https://docs.oracle.com/javase/specs/jls/se23/html/jls-4.html#jls-4.5.1 Java Language Specification, section
1480   * 4.5.1
1481   */
1482  public WildcardType wildcardType(TypeMirror extendsBound, TypeMirror superBound);
1483
1484}