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