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}