001/* -*- mode: Java; c-basic-offset: 2; indent-tabs-mode: nil; coding: utf-8-unix -*-
002 *
003 * Copyright © 2024–2026 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.constant.Constable;
017import java.lang.constant.DynamicConstantDesc;
018
019import java.util.List;
020import java.util.Objects;
021import java.util.Optional;
022
023import java.util.concurrent.locks.Lock;
024import java.util.concurrent.locks.ReentrantLock;
025
026import java.util.function.Supplier;
027
028import javax.annotation.processing.ProcessingEnvironment;
029
030import javax.lang.model.element.Element;
031import javax.lang.model.element.ExecutableElement;
032import javax.lang.model.element.ModuleElement;
033import javax.lang.model.element.Name;
034import javax.lang.model.element.Parameterizable;
035import javax.lang.model.element.TypeElement;
036
037import javax.lang.model.util.Elements;
038import javax.lang.model.util.Elements.Origin;
039import javax.lang.model.util.Types;
040
041import javax.lang.model.type.DeclaredType;
042import javax.lang.model.type.ExecutableType;
043import javax.lang.model.type.PrimitiveType;
044import javax.lang.model.type.TypeKind;
045import javax.lang.model.type.TypeMirror;
046
047import org.microbean.construct.element.StringName;
048import org.microbean.construct.element.UniversalElement;
049
050import org.microbean.construct.type.UniversalType;
051
052import static java.lang.constant.ConstantDescs.BSM_INVOKE;
053
054import static java.lang.constant.MethodHandleDesc.ofConstructor;
055
056/**
057 * A {@linkplain Domain domain of valid Java constructs} that can be used at annotation processing time or at runtime.
058 *
059 * @author <a href="https://about.me/lairdnelson" target="_top">Laird Nelson</a>
060 *
061 * @see Domain
062 *
063 * @see RuntimeProcessingEnvironmentSupplier
064 */
065@SuppressWarnings({ "try", "unchecked" })
066public class DefaultDomain implements Constable, Domain {
067
068  private final Supplier<? extends ProcessingEnvironment> pe;
069
070  private final Supplier<? extends Unlockable> locker;
071
072  /**
073   * Creates a new {@link DefaultDomain} <strong>for use at runtime</strong>.
074   *
075   * @see #DefaultDomain(ProcessingEnvironment, Lock)
076   */
077  public DefaultDomain() {
078    this(null, null);
079  }
080
081  /**
082   * Creates a new {@link DefaultDomain} <strong>normally for use at annotation processing time</strong>, whose usage
083   * type is actually determined by the argument supplied to this constructor.
084   *
085   * @param pe a {@link ProcessingEnvironment}; may be {@code null} (<strong>the expected value for runtime
086   * usage</strong>), in which case the return value of an invocation of {@link Supplier#get()} on the return value of
087   * an invocation of {@link RuntimeProcessingEnvironmentSupplier#of()} will be used instead
088   *
089   * @see #DefaultDomain(ProcessingEnvironment, Lock)
090   *
091   * @see RuntimeProcessingEnvironmentSupplier#get()
092   *
093   * @see SymbolCompletionLock#INSTANCE
094   */
095  public DefaultDomain(final ProcessingEnvironment pe) {
096    this(pe, null);
097  }
098
099  /**
100   * Creates a new {@link DefaultDomain} <strong>for use at runtime</strong>.
101   *
102   * @param lock a {@link Lock} to use to serialize symbol completion; may be {@code null} in which case a global {@link
103   * ReentrantLock} will be used instead
104   *
105   * @see #DefaultDomain(ProcessingEnvironment, Lock)
106   *
107   * @see RuntimeProcessingEnvironmentSupplier#get()
108   *
109   * @see SymbolCompletionLock#INSTANCE
110   */
111  public DefaultDomain(final Lock lock) {
112    this(null, lock);
113  }
114
115  /**
116   * Creates a new {@link DefaultDomain} <strong>normally for use at annotation processing time</strong>, whose usage
117   * type is actually determined by the arguments supplied to this constructor.
118   *
119   * @param pe a {@link ProcessingEnvironment}; may be {@code null} (<strong>the expected value for runtime
120   * usage</strong>), in which case the return value of an invocation of {@link Supplier#get()} on the return value of
121   * an invocation of {@link RuntimeProcessingEnvironmentSupplier#of()} will be used instead
122   *
123   * @param lock a {@link Lock} to use to serialize symbol completion; if {@code null} and {@code pe} is {@code null},
124   * then a global {@link ReentrantLock} will be used instead; if {@code null} and {@code pe} is non-{@code null}, then
125   * no serialization of symbol completion will occur <strong>and this {@link DefaultDomain} therefore will not be safe
126   * for concurrent use by multiple threads</strong>
127   *
128   * @see RuntimeProcessingEnvironmentSupplier#get()
129   *
130   * @see SymbolCompletionLock#INSTANCE
131   */
132  public DefaultDomain(final ProcessingEnvironment pe, final Lock lock) {
133    super();
134    if (pe == null) {
135      this.pe = RuntimeProcessingEnvironmentSupplier.of();
136      final Lock l = lock == null ? SymbolCompletionLock.INSTANCE : lock;
137      this.locker = () -> {
138        l.lock();
139        return l::unlock;
140      };
141    } else {
142      this.pe = () -> pe;
143      this.locker = lock == null ? DefaultDomain::noopLock : () -> {
144        lock.lock();
145        return lock::unlock;
146      };
147    }
148  }
149
150
151  /*
152   * Instance methods.
153   */
154
155
156  @Override // Domain
157  public List<? extends UniversalElement> allMembers(TypeElement e) {
158    e = unwrap(e);
159    try (var lock = lock()) {
160      return UniversalElement.of(this.elements().getAllMembers(e), this);
161    }
162  }
163
164  /**
165   * Returns a {@link UniversalType} representing an {@link javax.lang.model.type.ArrayType} whose {@linkplain
166   * javax.lang.model.type.ArrayType#getComponentType() component type} {@linkplain #sameType(TypeMirror, TypeMirror) is
167   * the same as} the supplied {@link TypeMirror}.
168   *
169   * @param t a {@link TypeMirror} representing a {@linkplain javax.lang.model.type.ArrayType#getComponentType()
170   * component type}; must not be {@code null}
171   *
172   * @return a non-{@code null} {@link UniversalType}
173   *
174   * @exception NullPointerException if {@code t} is {@code null}
175   *
176   * @exception IllegalArgumentException if {@code t} is unsuitable for use as a {@linkplain
177   * javax.lang.model.type.ArrayType#getComponentType() component type}
178   *
179   * @see Types#getArrayType(TypeMirror)
180   */
181  @Override // Domain
182  public UniversalType arrayTypeOf(TypeMirror t) {
183    t = unwrap(t);
184    try (var lock = lock()) {
185      return UniversalType.of(this.types().getArrayType(t), this);
186    }
187  }
188
189  @Override // Domain
190  public UniversalType asMemberOf(DeclaredType containingType, Element e) {
191    containingType = unwrap(containingType);
192    e = unwrap(e);
193    try (var lock = lock()) {
194      return UniversalType.of(this.types().asMemberOf(containingType, e), this);
195    }
196  }
197
198  @Override // Domain
199  // Note the strange positioning of payload and receiver.
200  public boolean assignable(TypeMirror payload, TypeMirror receiver) {
201    payload = unwrap(payload);
202    receiver = unwrap(receiver);
203    try (var lock = lock()) {
204      return this.types().isAssignable(payload, receiver);
205    }
206  }
207
208  @Override // Domain
209  public StringName binaryName(TypeElement e) {
210    e = unwrap(e);
211    try (var lock = lock()) {
212      return StringName.of(this.elements().getBinaryName(e).toString(), this);
213    }
214  }
215
216  @Override // Domain
217  public boolean bridge(ExecutableElement e) {
218    e = unwrap(e);
219    try (var lock = lock()) {
220      return this.elements().isBridge(e);
221    }
222  }
223
224  @Override // Domain
225  public UniversalType capture(TypeMirror t) {
226    t = unwrap(t);
227    try (var lock = lock()) {
228      return UniversalType.of(this.types().capture(t), this);
229    }
230  }
231
232  @Override // Domain
233  public boolean contains(TypeMirror t0, TypeMirror t1) {
234    t0 = unwrap(t0);
235    t1 = unwrap(t1);
236    try (var lock = lock()) {
237      return this.types().contains(t0, t1);
238    }
239  }
240
241  // (Convenience.)
242  @Override // Domain
243  public UniversalType declaredType(final CharSequence canonicalName) {
244    return UniversalType.of(Domain.super.declaredType(canonicalName), this);
245  }
246
247  @Override // Domain
248  public UniversalType declaredType(TypeElement typeElement,
249                                    TypeMirror... typeArguments) {
250    typeElement = unwrap(typeElement);
251    typeArguments = unwrap(typeArguments);
252    try (var lock = lock()) {
253      return UniversalType.of(this.types().getDeclaredType(typeElement, typeArguments), this);
254    }
255  }
256
257  @Override // Domain
258  public UniversalType declaredType(DeclaredType enclosingType,
259                                    TypeElement typeElement,
260                                    TypeMirror... typeArguments) {
261    enclosingType = unwrap(enclosingType);
262    typeElement = unwrap(typeElement);
263    typeArguments = unwrap(typeArguments);
264    try (var lock = lock()) {
265      return UniversalType.of(this.types().getDeclaredType(enclosingType, typeElement, typeArguments), this);
266    }
267  }
268
269  @Override // Constable
270  public Optional<DynamicConstantDesc<DefaultDomain>> describeConstable() {
271    return
272      Optional.of(DynamicConstantDesc.ofNamed(BSM_INVOKE,
273                                              this.getClass().getSimpleName(),
274                                              this.getClass().describeConstable().orElseThrow(),
275                                              ofConstructor(this.getClass().describeConstable().orElseThrow())));
276  }
277
278  @Override // Domain
279  public List<? extends UniversalType> directSupertypes(TypeMirror t) {
280    t = unwrap(t);
281    try (var lock = lock()) {
282      return UniversalType.of(this.types().directSupertypes(t), this);
283    }
284  }
285
286  @Override // Domain
287  public UniversalElement element(TypeMirror t) {
288    t = unwrap(t);
289    try (var lock = lock()) {
290      return UniversalElement.of(this.types().asElement(t), this);
291    }
292  }
293
294  @Override // Domain
295  public UniversalType elementType(final TypeMirror t) {
296    return UniversalType.of(Domain.super.elementType(t), this);
297  }
298
299  // Non-private for testing only.
300  final Elements elements() {
301    return this.pe().getElementUtils();
302  }
303
304  @Override // Object
305  public boolean equals(final Object other) {
306    if (this == other) {
307      return true;
308    } else if (other != null && other.getClass() == this.getClass()) {
309      final DefaultDomain her = (DefaultDomain)other;
310      return
311        Objects.equals(this.pe(), her.pe()) &&
312        Objects.equals(this.locker, her.locker);
313    } else {
314      return false;
315    }
316  }
317
318  @Override // Domain
319  public UniversalType erasure(TypeMirror t) {
320    t = unwrap(t);
321    try (var lock = lock()) {
322      return UniversalType.of(this.types().erasure(t), this);
323    }
324  }
325
326  // (Convenience.)
327  @Override // Domain
328  public UniversalElement executableElement(final TypeElement declaringElement,
329                                            final TypeMirror returnType,
330                                            final CharSequence name,
331                                            final TypeMirror... parameterTypes) {
332    return UniversalElement.of(Domain.super.executableElement(declaringElement, returnType, name, parameterTypes), this);
333  }
334
335  @Override // Object
336  public int hashCode() {
337    return this.pe().hashCode() ^ this.locker.hashCode();
338  }
339
340  // (Convenience.)
341  @Override // Domain
342  public UniversalElement javaLangObject() {
343    return UniversalElement.of(Domain.super.javaLangObject(), this);
344  }
345
346  // (Convenience.)
347  @Override // Domain
348  public UniversalType javaLangObjectType() {
349    return UniversalType.of(Domain.super.javaLangObjectType(), this);
350  }
351
352  /**
353   * Returns a non-{@code null} {@link Unlockable} that should be used in a {@code try}-with-resources block guarding
354   * operations that might cause symbol completion.
355   *
356   * @return a non-{@code null} {@link Unlockable}
357   *
358   * @see Unlockable#close()
359   */
360  public final Unlockable lock() {
361    return this.locker.get();
362  }
363
364  // (Canonical.)
365  @Override // Domain
366  public UniversalElement moduleElement(final CharSequence canonicalName) {
367    try (var lock = lock()) {
368      return UniversalElement.of(this.elements().getModuleElement(canonicalName), this);
369    }
370  }
371
372  // (Canonical.)
373  @Override // Domain
374  public StringName name(final CharSequence name) {
375    return switch (name) {
376    case StringName sn -> sn;
377    // You will be tempted to add other efficient cases here. Do not.
378    default -> {
379      try (var lock = lock()) {
380        yield StringName.of(this.elements().getName(name).toString(), this);
381      }
382    }
383    };
384  }
385
386  // (Canonical.)
387  @Override // Domain
388  public UniversalType noType(final TypeKind kind) {
389    return UniversalType.of(this.types().getNoType(kind), this);
390  }
391
392  // (Canonical.)
393  @Override // Domain
394  public UniversalType nullType() {
395    return UniversalType.of(this.types().getNullType(), this);
396  }
397
398  @Override // Domain
399  public Origin origin(Element e) {
400    e = unwrap(e);
401    try (var lock = lock()) {
402      return this.elements().getOrigin(e);
403    }
404  }
405
406  // (Canonical.)
407  @Override // Domain
408  public UniversalElement packageElement(final CharSequence canonicalName) {
409    try (var lock = lock()) {
410      return UniversalElement.of(this.elements().getPackageElement(canonicalName), this);
411    }
412  }
413
414  // (Canonical.)
415  @Override // Domain
416  public UniversalElement packageElement(ModuleElement asSeenFrom, final CharSequence canonicalName) {
417    asSeenFrom = unwrap(asSeenFrom);
418    try (var lock = lock()) {
419      return UniversalElement.of(this.elements().getPackageElement(asSeenFrom, canonicalName), this);
420    }
421  }
422
423  private final ProcessingEnvironment pe() {
424    return this.pe.get();
425  }
426
427  // (Canonical.)
428  @Override // Domain
429  public UniversalType primitiveType(final TypeKind kind) {
430    return UniversalType.of(this.types().getPrimitiveType(kind), this);
431  }
432
433  // (Convenience.)
434  // (Unboxing.)
435  @Override // Domain
436  public UniversalType primitiveType(final CharSequence canonicalName) {
437    return UniversalType.of(Domain.super.primitiveType(canonicalName), this);
438  }
439
440  // (Convenience.)
441  // (Unboxing.)
442  @Override // Domain
443  public UniversalType primitiveType(final TypeElement e) {
444    return UniversalType.of(Domain.super.primitiveType(e), this);
445  }
446
447  // (Canonical.)
448  // (Unboxing.)
449  @Override // Domain
450  public UniversalType primitiveType(TypeMirror t) {
451    t = unwrap(t);
452    try (var lock = lock()) {
453      return UniversalType.of(this.types().unboxedType(t), this);
454    }
455  }
456
457  @Override // Domain
458  public UniversalType rawType(final TypeMirror t) {
459    return UniversalType.of(Domain.super.rawType(t), this);
460  }
461
462  // (Canonical.)
463  @Override // Domain
464  public UniversalElement recordComponentElement(ExecutableElement e) {
465    e = unwrap(e);
466    try (var lock = lock()) {
467      return UniversalElement.of(this.elements().recordComponentFor(e), this);
468    }
469  }
470
471  @Override // Domain
472  public boolean sameType(TypeMirror t0, TypeMirror t1) {
473    if (t0 == t1) {
474      // Critical; javax.lang.model.Types#isSameType(TypeMirror, TypeMirror) returns false here, on purpose, if the two
475      // identical references are wildcards. This is almost never what anyone wants.
476      return true;
477    } else if (t0 == null || t1 == null) {
478      return false;
479    }
480    t0 = unwrap(t0);
481    t1 = unwrap(t1);
482    try (var lock = lock()) {
483      return this.types().isSameType(t0, t1);
484    }
485  }
486
487  @Override // Domain
488  public boolean subsignature(ExecutableType t0, ExecutableType t1) {
489    t0 = unwrap(t0);
490    t1 = unwrap(t1);
491    try (var lock = lock()) {
492      return this.types().isSubsignature(t0, t1);
493    }
494  }
495
496  @Override // Domain
497  public boolean subtype(TypeMirror candidateSubtype, TypeMirror candidateSupertype) {
498    candidateSubtype = unwrap(candidateSubtype);
499    candidateSupertype = unwrap(candidateSupertype);
500    try (var lock = lock()) {
501      return this.types().isSubtype(candidateSubtype, candidateSupertype);
502    }
503  }
504
505  @Override // Domain
506  public String toString(final CharSequence name) {
507    return switch (name) {
508    case null -> null;
509    case String s -> s;
510    case StringName sn -> sn.value();
511    case Name n -> {
512      try (var lock = lock()) {
513        yield n.toString();
514      }
515    }
516    default -> name.toString();
517    };
518  }
519
520  // (Canonical.)
521  @Override // Domain
522  public UniversalElement typeElement(final CharSequence canonicalName) {
523    try (var lock = lock()) {
524      return UniversalElement.of(this.elements().getTypeElement(switch (canonicalName.toString()) {
525          case "boolean" -> "java.lang.Boolean";
526          case "byte" -> "java.lang.Byte";
527          case "char" -> "java.lang.Character";
528          case "double" -> "java.lang.Double";
529          case "float" -> "java.lang.Float";
530          case "int" -> "java.lang.Integer";
531          case "long" -> "java.lang.Long";
532          case "short" -> "java.lang.Short";
533          default -> canonicalName;
534          }), this);
535    }
536  }
537
538  // (Canonical.)
539  @Override // Domain
540  public UniversalElement typeElement(ModuleElement asSeenFrom, final CharSequence canonicalName) {
541    asSeenFrom = unwrap(asSeenFrom);
542    try (var lock = lock()) {
543      return UniversalElement.of(this.elements().getTypeElement(asSeenFrom, switch (canonicalName.toString()) {
544          case "boolean" -> "java.lang.Boolean";
545          case "byte" -> "java.lang.Byte";
546          case "char" -> "java.lang.Character";
547          case "double" -> "java.lang.Double";
548          case "float" -> "java.lang.Float";
549          case "int" -> "java.lang.Integer";
550          case "long" -> "java.lang.Long";
551          case "short" -> "java.lang.Short";
552          default -> canonicalName;
553          }), this);
554    }
555  }
556
557  // (Canonical.)
558  // (Boxing.)
559  @Override // Domain
560  public UniversalElement typeElement(PrimitiveType t) {
561    t = unwrap(t);
562    try (var lock = lock()) {
563      return UniversalElement.of(this.types().boxedClass(t), this);
564    }
565  }
566
567  // (Convenience.)
568  // (Boxing.)
569  @Override // Domain
570  public UniversalElement typeElement(final TypeKind primitiveTypeKind) {
571    return UniversalElement.of(Domain.super.typeElement(primitiveTypeKind), this);
572  }
573
574  // (Convenience.)
575  @Override // Domain
576  public UniversalElement typeParameterElement(final Parameterizable p, final CharSequence name) {
577    return UniversalElement.of(Domain.super.typeParameterElement(p, name), this);
578  }
579
580  // Non-private for testing only.
581  final Types types() {
582    return this.pe().getTypeUtils();
583  }
584
585  // (Convenience.)
586  @Override // Domain
587  public UniversalType typeVariable(final Parameterizable p, final CharSequence name) {
588    return UniversalType.of(Domain.super.typeVariable(p, name), this);
589  }
590
591  // (Convenience.)
592  @Override // Domain
593  public UniversalElement variableElement(final Element e, final CharSequence name) {
594    return UniversalElement.of(Domain.super.variableElement(e, name), this);
595  }
596
597  @Override // Domain
598  public UniversalType wildcardType() {
599    return this.wildcardType(null, null);
600  }
601
602  @Override // Domain
603  public UniversalType wildcardType(TypeMirror extendsBound, TypeMirror superBound) {
604    extendsBound = unwrap(extendsBound);
605    superBound = unwrap(superBound);
606    try (var lock = lock()) {
607      return UniversalType.of(this.types().getWildcardType(extendsBound, superBound), this);
608    }
609  }
610
611
612  /*
613   * Static methods.
614   */
615
616
617  // (Invoked only by method reference.)
618  private static final void doNothing() {}
619
620  // (Invoked only by method reference.)
621  private static final Unlockable noopLock() {
622    return DefaultDomain::doNothing;
623  }
624
625  private static final <T extends TypeMirror> T unwrap(final T t) {
626    return UniversalType.unwrap(t);
627  }
628
629  private static final <E extends Element> E unwrap(final E e) {
630    return UniversalElement.unwrap(e);
631  }
632
633  private static final TypeMirror[] unwrap(final TypeMirror[] ts) {
634    if (ts == null || ts.length == 0) {
635      return ts;
636    }
637    final TypeMirror[] rv = new TypeMirror[ts.length];
638    for (int i = 0; i < ts.length; i++) {
639      rv[i] = unwrap(ts[i]);
640    }
641    return rv;
642  }
643
644}