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