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.TypeElement; 039 040import javax.lang.model.util.Elements; 041import javax.lang.model.util.Elements.Origin; 042import javax.lang.model.util.Types; 043 044import javax.lang.model.type.DeclaredType; 045import javax.lang.model.type.ExecutableType; 046import javax.lang.model.type.PrimitiveType; 047import javax.lang.model.type.TypeKind; 048import javax.lang.model.type.TypeMirror; 049 050import org.microbean.construct.element.StringName; 051import org.microbean.construct.element.UniversalElement; 052 053import org.microbean.construct.type.UniversalType; 054 055import static java.lang.constant.ConstantDescs.BSM_INVOKE; 056 057/** 058 * A {@linkplain Domain domain of Java constructs} that can be used at annotation processing time or at runtime. 059 * 060 * @author <a href="https://about.me/lairdnelson" target="_top">Laird Nelson</a> 061 * 062 * @see Domain 063 * 064 * @see RuntimeProcessingEnvironmentSupplier 065 */ 066@SuppressWarnings({ "try", "unchecked" }) 067public class DefaultDomain implements Constable, Domain { 068 069 private final Supplier<? extends ProcessingEnvironment> pe; 070 071 private final Supplier<? extends Unlockable> locker; 072 073 /** 074 * Creates a new {@link DefaultDomain} <strong>for use at runtime</strong>. 075 * 076 * @see #DefaultDomain(ProcessingEnvironment, Lock) 077 */ 078 public DefaultDomain() { 079 this(null, null); 080 } 081 082 /** 083 * Creates a new {@link DefaultDomain} <strong>normally for use at annotation processing time</strong>, whose usage 084 * type is actually determined by the argument supplied to this constructor. 085 * 086 * @param pe a {@link ProcessingEnvironment}; may be {@code null} in which case the return value of an invocation of 087 * {@link Supplier#get()} on the return value of an invocation of {@link RuntimeProcessingEnvironmentSupplier#of()} 088 * will be used instead 089 * 090 * @see #DefaultDomain(ProcessingEnvironment, Lock) 091 * 092 * @see SymbolCompletionLock 093 */ 094 public DefaultDomain(final ProcessingEnvironment pe) { 095 this(pe, null); 096 } 097 098 /** 099 * Creates a new {@link DefaultDomain} <strong>for use at runtime</strong>. 100 * 101 * @param lock a {@link Lock} to use to serialize symbol completion; may be {@code null} in which case a global {@link 102 * ReentrantLock} will be used instead 103 * 104 * @see #DefaultDomain(ProcessingEnvironment, Lock) 105 * 106 * @see RuntimeProcessingEnvironmentSupplier 107 * 108 * @see SymbolCompletionLock 109 */ 110 public DefaultDomain(final Lock lock) { 111 this(null, lock); 112 } 113 114 /** 115 * Creates a new {@link DefaultDomain} <strong>normally for use at annotation processing time</strong>, whose usage 116 * type is actually 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 <strong>and this {@link DefaultDomain} therefore will not be safe 125 * for concurrent use by multiple threads</strong> 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 // Non-private for testing only. 289 final Elements elements() { 290 return this.pe().getElementUtils(); 291 } 292 293 @Override // Object 294 public boolean equals(final Object other) { 295 if (this == other) { 296 return true; 297 } else if (other != null && other.getClass() == this.getClass()) { 298 final DefaultDomain her = (DefaultDomain)other; 299 return 300 Objects.equals(this.pe(), her.pe()) && 301 Objects.equals(this.locker, her.locker); 302 } else { 303 return false; 304 } 305 } 306 307 @Override // Domain 308 public UniversalType erasure(TypeMirror t) { 309 t = unwrap(t); 310 try (var lock = lock()) { 311 return UniversalType.of(this.types().erasure(t), this); 312 } 313 } 314 315 // (Convenience.) 316 @Override // Domain 317 public UniversalElement executableElement(final TypeElement declaringElement, 318 final TypeMirror returnType, 319 final CharSequence name, 320 final TypeMirror... parameterTypes) { 321 return UniversalElement.of(Domain.super.executableElement(declaringElement, returnType, name, parameterTypes), this); 322 } 323 324 @Override // Object 325 public int hashCode() { 326 return this.pe().hashCode() ^ this.locker.hashCode(); 327 } 328 329 // (Convenience.) 330 @Override // Domain 331 public UniversalElement javaLangObject() { 332 return UniversalElement.of(Domain.super.javaLangObject(), this); 333 } 334 335 // (Convenience.) 336 @Override // Domain 337 public UniversalType javaLangObjectType() { 338 return UniversalType.of(Domain.super.javaLangObjectType(), this); 339 } 340 341 /** 342 * Returns a non-{@code null} {@link Unlockable} that should be used in a {@code try}-with-resources block guarding 343 * operations that might cause symbol completion. 344 * 345 * @return a non-{@code null} {@link Unlockable} 346 * 347 * @see Unlockable#close() 348 */ 349 public final Unlockable lock() { 350 return this.locker.get(); 351 } 352 353 // (Canonical.) 354 @Override // Domain 355 public UniversalElement moduleElement(final CharSequence canonicalName) { 356 try (var lock = lock()) { 357 return UniversalElement.of(this.elements().getModuleElement(canonicalName), this); 358 } 359 } 360 361 // (Canonical.) 362 @Override // Domain 363 public StringName name(final CharSequence name) { 364 return switch (name) { 365 case StringName sn -> sn; 366 // You will be tempted to add other efficient cases here. Do not. 367 default -> { 368 try (var lock = lock()) { 369 yield StringName.of(this.elements().getName(name).toString(), this); 370 } 371 } 372 }; 373 } 374 375 // (Canonical.) 376 @Override // Domain 377 public UniversalType noType(final TypeKind kind) { 378 return UniversalType.of(this.types().getNoType(kind), this); 379 } 380 381 // (Canonical.) 382 @Override // Domain 383 public UniversalType nullType() { 384 return UniversalType.of(this.types().getNullType(), this); 385 } 386 387 @Override // Domain 388 public Origin origin(Element e) { 389 e = unwrap(e); 390 try (var lock = lock()) { 391 return this.elements().getOrigin(e); 392 } 393 } 394 395 // (Canonical.) 396 @Override // Domain 397 public UniversalElement packageElement(final CharSequence canonicalName) { 398 try (var lock = lock()) { 399 return UniversalElement.of(this.elements().getPackageElement(canonicalName), this); 400 } 401 } 402 403 // (Canonical.) 404 @Override // Domain 405 public UniversalElement packageElement(ModuleElement asSeenFrom, final CharSequence canonicalName) { 406 asSeenFrom = unwrap(asSeenFrom); 407 try (var lock = lock()) { 408 return UniversalElement.of(this.elements().getPackageElement(asSeenFrom, canonicalName), this); 409 } 410 } 411 412 private final ProcessingEnvironment pe() { 413 return this.pe.get(); 414 } 415 416 // (Canonical.) 417 @Override // Domain 418 public UniversalType primitiveType(final TypeKind kind) { 419 return UniversalType.of(this.types().getPrimitiveType(kind), this); 420 } 421 422 // (Convenience.) 423 // (Unboxing.) 424 @Override // Domain 425 public UniversalType primitiveType(final CharSequence canonicalName) { 426 return UniversalType.of(Domain.super.primitiveType(canonicalName), this); 427 } 428 429 // (Convenience.) 430 // (Unboxing.) 431 @Override // Domain 432 public UniversalType primitiveType(final TypeElement e) { 433 return UniversalType.of(Domain.super.primitiveType(e), this); 434 } 435 436 // (Canonical.) 437 // (Unboxing.) 438 @Override // Domain 439 public UniversalType primitiveType(TypeMirror t) { 440 t = unwrap(t); 441 try (var lock = lock()) { 442 return UniversalType.of(this.types().unboxedType(t), this); 443 } 444 } 445 446 @Override // Domain 447 public UniversalType rawType(final TypeMirror t) { 448 return UniversalType.of(Domain.super.rawType(t), this); 449 } 450 451 // (Canonical.) 452 @Override // Domain 453 public UniversalElement recordComponentElement(ExecutableElement e) { 454 e = unwrap(e); 455 try (var lock = lock()) { 456 return UniversalElement.of(this.elements().recordComponentFor(e), this); 457 } 458 } 459 460 @Override // Domain 461 public boolean sameType(TypeMirror t0, TypeMirror t1) { 462 if (t0 == t1) { 463 // Critical; javax.lang.model.Types#isSameType(TypeMirror, TypeMirror) returns false here, on purpose, if the two 464 // identical references are wildcards. This is almost never what anyone wants. 465 return true; 466 } else if (t0 == null || t1 == null) { 467 return false; 468 } 469 t0 = unwrap(t0); 470 t1 = unwrap(t1); 471 try (var lock = lock()) { 472 return this.types().isSameType(t0, t1); 473 } 474 } 475 476 @Override // Domain 477 public boolean subsignature(ExecutableType t0, ExecutableType t1) { 478 t0 = unwrap(t0); 479 t1 = unwrap(t1); 480 try (var lock = lock()) { 481 return this.types().isSubsignature(t0, t1); 482 } 483 } 484 485 @Override // Domain 486 public boolean subtype(TypeMirror candidateSubtype, TypeMirror candidateSupertype) { 487 candidateSubtype = unwrap(candidateSubtype); 488 candidateSupertype = unwrap(candidateSupertype); 489 try (var lock = lock()) { 490 return this.types().isSubtype(candidateSubtype, candidateSupertype); 491 } 492 } 493 494 @Override // Domain 495 public String toString(final CharSequence name) { 496 return switch (name) { 497 case null -> null; 498 case String s -> s; 499 case StringName sn -> sn.value(); 500 case Name n -> { 501 try (var lock = lock()) { 502 yield n.toString(); 503 } 504 } 505 default -> name.toString(); 506 }; 507 } 508 509 // (Canonical.) 510 @Override // Domain 511 public UniversalElement typeElement(final CharSequence canonicalName) { 512 try (var lock = lock()) { 513 return UniversalElement.of(this.elements().getTypeElement(switch (canonicalName.toString()) { 514 case "boolean" -> "java.lang.Boolean"; 515 case "byte" -> "java.lang.Byte"; 516 case "char" -> "java.lang.Character"; 517 case "double" -> "java.lang.Double"; 518 case "float" -> "java.lang.Float"; 519 case "int" -> "java.lang.Integer"; 520 case "long" -> "java.lang.Long"; 521 case "short" -> "java.lang.Short"; 522 default -> canonicalName; 523 }), this); 524 } 525 } 526 527 // (Canonical.) 528 @Override // Domain 529 public UniversalElement typeElement(ModuleElement asSeenFrom, final CharSequence canonicalName) { 530 asSeenFrom = unwrap(asSeenFrom); 531 try (var lock = lock()) { 532 return UniversalElement.of(this.elements().getTypeElement(asSeenFrom, switch (canonicalName.toString()) { 533 case "boolean" -> "java.lang.Boolean"; 534 case "byte" -> "java.lang.Byte"; 535 case "char" -> "java.lang.Character"; 536 case "double" -> "java.lang.Double"; 537 case "float" -> "java.lang.Float"; 538 case "int" -> "java.lang.Integer"; 539 case "long" -> "java.lang.Long"; 540 case "short" -> "java.lang.Short"; 541 default -> canonicalName; 542 }), this); 543 } 544 } 545 546 // (Canonical.) 547 // (Boxing.) 548 @Override // Domain 549 public UniversalElement typeElement(PrimitiveType t) { 550 t = unwrap(t); 551 try (var lock = lock()) { 552 return UniversalElement.of(this.types().boxedClass(t), this); 553 } 554 } 555 556 // (Convenience.) 557 // (Boxing.) 558 @Override // Domain 559 public UniversalElement typeElement(final TypeKind primitiveTypeKind) { 560 return UniversalElement.of(Domain.super.typeElement(primitiveTypeKind), this); 561 } 562 563 // (Convenience.) 564 @Override // Domain 565 public UniversalElement typeParameterElement(final Parameterizable p, final CharSequence name) { 566 return UniversalElement.of(Domain.super.typeParameterElement(p, name), this); 567 } 568 569 // Non-private for testing only. 570 final Types types() { 571 return this.pe().getTypeUtils(); 572 } 573 574 // (Convenience.) 575 @Override // Domain 576 public UniversalType typeVariable(final Parameterizable p, final CharSequence name) { 577 return UniversalType.of(Domain.super.typeVariable(p, name), this); 578 } 579 580 // (Convenience.) 581 @Override // Domain 582 public UniversalElement variableElement(final Element e, final CharSequence name) { 583 return UniversalElement.of(Domain.super.variableElement(e, name), this); 584 } 585 586 @Override // Domain 587 public UniversalType wildcardType() { 588 return this.wildcardType(null, null); 589 } 590 591 @Override // Domain 592 public UniversalType wildcardType(TypeMirror extendsBound, TypeMirror superBound) { 593 extendsBound = unwrap(extendsBound); 594 superBound = unwrap(superBound); 595 try (var lock = lock()) { 596 return UniversalType.of(this.types().getWildcardType(extendsBound, superBound), this); 597 } 598 } 599 600 601 /* 602 * Static methods. 603 */ 604 605 606 private static final void doNothing() {} 607 608 private static final Unlockable noopLock() { 609 return DefaultDomain::doNothing; 610 } 611 612 private static final <T extends TypeMirror> T unwrap(final T t) { 613 return UniversalType.unwrap(t); 614 } 615 616 private static final <E extends Element> E unwrap(final E e) { 617 return UniversalElement.unwrap(e); 618 } 619 620 private static final TypeMirror[] unwrap(final TypeMirror[] ts) { 621 if (ts == null || ts.length <= 0) { 622 return ts; 623 } 624 final TypeMirror[] rv = new TypeMirror[ts.length]; 625 for (int i = 0; i < ts.length; i++) { 626 rv[i] = unwrap(ts[i]); 627 } 628 return rv; 629 } 630 631}