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