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