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.annotation.Annotation; 017 018import java.lang.constant.ClassDesc; 019import java.lang.constant.Constable; 020import java.lang.constant.ConstantDesc; 021import java.lang.constant.DynamicConstantDesc; 022 023import java.util.List; 024import java.util.Optional; 025 026import java.util.concurrent.CopyOnWriteArrayList; 027 028import java.util.function.Supplier; 029 030import javax.lang.model.AnnotatedConstruct; 031 032import javax.lang.model.element.AnnotationMirror; 033import javax.lang.model.element.Element; 034import javax.lang.model.element.QualifiedNameable; 035 036import javax.lang.model.type.TypeMirror; 037 038import org.microbean.construct.constant.Constables; 039 040import org.microbean.construct.element.UniversalAnnotation; 041import org.microbean.construct.element.UniversalElement; 042 043import org.microbean.construct.type.UniversalType; 044 045import static java.lang.constant.ConstantDescs.BSM_INVOKE; 046import static java.lang.constant.ConstantDescs.CD_List; 047 048import static java.lang.constant.MethodHandleDesc.ofConstructor; 049 050import static java.util.Objects.requireNonNull; 051 052/** 053 * An abstract implementation of {@link AnnotatedConstruct} from which only {@link UniversalElement} and {@link 054 * UniversalType} descend. 055 * 056 * @param <T> a type of {@link AnnotatedConstruct}, which may be only either {@link Element} or {@link TypeMirror} 057 * 058 * @author <a href="https://about.me/lairdnelson" target="_top">Laird Nelson</a> 059 * 060 * @see AnnotatedConstruct 061 * 062 * @see UniversalElement 063 * 064 * @see UniversalType 065 */ 066public abstract sealed class UniversalConstruct<T extends AnnotatedConstruct> 067 implements AnnotatedConstruct, Constable 068 permits UniversalElement, UniversalType { 069 070 071 /* 072 * Static fields. 073 */ 074 075 076 private static final Annotation[] EMPTY_ANNOTATION_ARRAY = new Annotation[0]; 077 078 079 /* 080 * Instance fields. 081 */ 082 083 084 private final PrimordialDomain domain; 085 086 // Eventually this should become a lazy constant/stable value 087 // volatile not needed 088 private Supplier<? extends T> delegateSupplier; 089 090 // Eventually this should become a lazy constant/stable value 091 private volatile String s; 092 093 // Eventually this should become a lazy constant/stable value 094 private volatile CopyOnWriteArrayList<AnnotationMirror> annotations; 095 096 097 /* 098 * Constructors. 099 */ 100 101 102 /** 103 * Creates a new {@link AnnotatedConstruct}. 104 * 105 * @param delegate a delegate to which operations are delegated; must not be {@code null} 106 * 107 * @param domain a {@link PrimordialDomain} representing the construct domain from which the supplied {@code 108 * delegate} is presumed to have originated; must not be {@code null} 109 * 110 * @exception NullPointerException if either argument is {@code null} 111 * 112 * @see #UniversalConstruct(List, AnnotatedConstruct, PrimordialDomain) 113 */ 114 protected UniversalConstruct(final T delegate, final PrimordialDomain domain) { 115 this(null, delegate, domain); 116 } 117 118 /** 119 * Creates a new {@link AnnotatedConstruct}. 120 * 121 * @param annotations a {@link List} of {@link AnnotationMirror} instances representing annotations, often 122 * synthetic, that this {@link UniversalConstruct} should bear; may be {@code null} in which case only the annotations 123 * from the supplied {@code delegate} will be used 124 * @param delegate a delegate to which operations are delegated; must not be {@code null} 125 * 126 * @param domain a {@link PrimordialDomain} representing the construct domain from which the supplied {@code 127 * delegate} is presumed to have originated; must not be {@code null} 128 * 129 * @exception NullPointerException if any argument is {@code null} 130 */ 131 @SuppressWarnings("try") 132 protected UniversalConstruct(final List<? extends AnnotationMirror> annotations, 133 final T delegate, 134 final PrimordialDomain domain) { 135 super(); 136 this.domain = requireNonNull(domain, "domain"); 137 if (annotations != null) { 138 this.annotations = new CopyOnWriteArrayList<>(annotations); 139 } 140 final T unwrappedDelegate = unwrap(requireNonNull(delegate, "delegate")); 141 if (unwrappedDelegate == delegate) { 142 this.delegateSupplier = () -> { 143 try (var lock = domain.lock()) { 144 // No unwrapping happened so do symbol completion early; most common case. 145 if (unwrappedDelegate instanceof Element) { 146 ((Element)unwrappedDelegate).getModifiers(); 147 } else { 148 ((TypeMirror)unwrappedDelegate).getKind(); 149 } 150 this.delegateSupplier = () -> unwrappedDelegate; 151 } 152 return unwrappedDelegate; 153 }; 154 } else { 155 assert delegate instanceof UniversalConstruct; 156 // Symbol completion already happened because unwrapping actually happened 157 this.delegateSupplier = () -> unwrappedDelegate; 158 } 159 } 160 161 162 /* 163 * Instance methods. 164 */ 165 166 167 /** 168 * Returns the delegate to which operations are delegated. 169 * 170 * <p>The delegate is guaranteed not to be an instance of {@link UniversalConstruct}.</p> 171 * 172 * @return a non-{@code null} delegate 173 * 174 * @see Element 175 * 176 * @see TypeMirror 177 */ 178 public final T delegate() { 179 final T delegate = this.delegateSupplier.get(); 180 assert !(delegate instanceof UniversalConstruct); 181 return delegate; 182 } 183 184 @Override // Constable 185 public final Optional<? extends ConstantDesc> describeConstable() { 186 final PrimordialDomain primordialDomain = this.domain(); 187 if (domain instanceof Domain d && d instanceof Constable dc) { 188 final T delegate = this.delegate(); 189 final List<AnnotationMirror> annotations = this.annotations; // volatile read; may be null and that's OK 190 return Constables.describe(delegate, d) 191 .flatMap(delegateDesc -> Constables.describe(annotations) 192 .map(annosDesc -> DynamicConstantDesc.of(BSM_INVOKE, 193 ofConstructor(ClassDesc.of(this.getClass().getName()), 194 CD_List, 195 ClassDesc.of(delegate instanceof TypeMirror ? TypeMirror.class.getName() : Element.class.getName()), 196 ClassDesc.of(PrimordialDomain.class.getName())), 197 annosDesc, 198 delegateDesc, 199 dc.describeConstable().orElseThrow()))); 200 } 201 return Optional.empty(); 202 } 203 204 /** 205 * Returns the {@link PrimordialDomain} supplied at construction time. 206 * 207 * @return the non-{@code null} {@link PrimordialDomain} supplied at construction time 208 */ 209 public final PrimordialDomain domain() { 210 return this.domain; 211 } 212 213 @Override // Object 214 public final boolean equals(final Object other) { 215 // Interesting; equality does not cause symbol completion. See: 216 // 217 // https://github.com/openjdk/jdk/blob/jdk-26%2B25/src/jdk.compiler/share/classes/com/sun/tools/javac/code/Type.java#L553-L559 218 // (the only type that overrides this is ArrayType; see 219 // https://github.com/openjdk/jdk/blob/jdk-26%2B25/src/jdk.compiler/share/classes/com/sun/tools/javac/code/Type.java#L1402-L1406) 220 // 221 // https://github.com/openjdk/jdk/blob/jdk-26%2B25/src/jdk.compiler/share/classes/com/sun/tools/javac/code/Symbol.java 222 // (Symbol (Element) doesn't override it at all.) 223 return this == other || switch (other) { 224 case null -> false; 225 case UniversalConstruct<?> uc when this.getClass() == uc.getClass() -> this.delegate().equals(uc.delegate()); 226 default -> false; 227 }; 228 } 229 230 /** 231 * Returns a non-{@code null}, determinate, <strong>mutable</strong>, thread-safe {@link List} of {@link 232 * AnnotationMirror} instances representing the annotations to be considered <dfn>directly present</dfn> on this 233 * {@link UniversalConstruct} implementation. 234 * 235 * @return a non-{@code null}, determinate, <strong>mutable</strong> thread-safe {@link List} of {@link 236 * AnnotationMirror}s 237 * 238 * @see AnnotatedConstruct#getAnnotationMirrors() 239 */ 240 @Override // AnnotatedConstruct 241 @SuppressWarnings("try") 242 public final List<AnnotationMirror> getAnnotationMirrors() { 243 CopyOnWriteArrayList<AnnotationMirror> annotations = this.annotations; // volatile read 244 if (annotations == null) { 245 try (var lock = this.domain().lock()) { 246 this.annotations = annotations = // volatile write 247 new CopyOnWriteArrayList<>(UniversalAnnotation.of(this.delegate().getAnnotationMirrors(), this.domain())); 248 } 249 } 250 return annotations; 251 } 252 253 /** 254 * Makes a <strong>best effort</strong> to return an {@link Annotation} of the appropriate type <dfn>present</dfn> on 255 * this {@link UniversalConstruct} implementation. 256 * 257 * <p>See the specification for the {@link AnnotatedConstruct#getAnnotation(Class)} method for important details.</p> 258 * 259 * <p>{@link UniversalConstruct} implementations deliberately permit modification of their {@linkplain 260 * #getAnnotationMirrors() annotations}. Consequently, this override first checks to see if there is at least one 261 * {@link AnnotationMirror} whose {@linkplain AnnotationMirror#getAnnotationType() annotation type} is declared by a 262 * {@link javax.lang.model.element.TypeElement} whose {@linkplain 263 * javax.lang.model.element.TypeElement#getQualifiedName() qualified name} is {@linkplain 264 * javax.lang.model.element.Name#contentEquals(CharSequence) equal to} the {@linkplain Class#getCanonicalName() 265 * canonical name} of the supplied {@link Class}. If there is, then the {@link AnnotatedConstruct#getAnnotation(Class) 266 * getAnnotation(Class)} method is invoked on the {@linkplain #delegate() delegate} and its result is 267 * returned. Otherwise, {@code null} is returned.</p> 268 * 269 * <p>There are circumstances where the {@link Annotation} returned by this method may not accurately reflect a 270 * synthetic annotation added to this {@link AnnotatedConstruct} implementation's {@linkplain #getAnnotationMirrors() 271 * annotations}.</p> 272 * 273 * <p>In general, the use of this method is discouraged.</p> 274 * 275 * @param annotationType a {@link Class} that is an annotation interface; must not be {@code null} 276 * 277 * @return an appropriate {@link Annotation}, or {@code null} 278 * 279 * @exception NullPointerException if {@code annotationType} is {@code null} 280 * 281 * @see AnnotatedConstruct#getAnnotation(Class) 282 * 283 * @deprecated The use of this method is discouraged. 284 */ 285 @Deprecated 286 @Override // AnnotatedConstruct 287 @SuppressWarnings("try") 288 public final <A extends Annotation> A getAnnotation(final Class<A> annotationType) { 289 if (!annotationType.isAnnotation()) { 290 return null; 291 } 292 final String canonicalName = annotationType.getCanonicalName(); 293 for (final AnnotationMirror a : this.getAnnotationMirrors()) { 294 if (((QualifiedNameable)a.getAnnotationType().asElement()).getQualifiedName().contentEquals(canonicalName)) { 295 // TODO: is this lock actually needed, given how delegateSupplier works? 296 try (var lock = this.domain().lock()) { 297 return this.delegate().getAnnotation(annotationType); 298 } 299 } 300 } 301 return null; 302 303 } 304 305 /** 306 * Makes a <strong>best effort</strong> to return an array of {@link Annotation}s of the appropriate type 307 * <dfn>associated</dfn> with this {@link UniversalConstruct} implementation. 308 * 309 * <p>See the specification for the {@link AnnotatedConstruct#getAnnotationsByType(Class)} method for important 310 * details.</p> 311 * 312 * <p>{@link UniversalConstruct} implementations deliberately permit modification of their {@linkplain 313 * #getAnnotationMirrors() annotations}. Consequently, this override first checks to see if there is at least one 314 * {@link AnnotationMirror} whose {@linkplain AnnotationMirror#getAnnotationType() annotation type} is declared by a 315 * {@link javax.lang.model.element.TypeElement} whose {@linkplain 316 * javax.lang.model.element.TypeElement#getQualifiedName() qualified name} is {@linkplain 317 * javax.lang.model.element.Name#contentEquals(CharSequence) equal to} the {@linkplain Class#getCanonicalName() 318 * canonical name} of the supplied {@link Class}. If there is, then the {@link 319 * AnnotatedConstruct#getAnnotationsByType(Class) getAnnotationsByType(Class)} method is invoked on the {@linkplain 320 * #delegate() delegate} and its result is returned. Otherwise, an empty array is returned.</p> 321 * 322 * <p>There are circumstances where the {@link Annotation} array returned by this method may not accurately reflect 323 * synthetic annotations added to this {@link AnnotatedConstruct} implementation's {@linkplain #getAnnotationMirrors() 324 * annotations}.</p> 325 * 326 * <p>In general, the use of this method is discouraged.</p> 327 * 328 * @param annotationType a {@link Class} that is an annotation interface; must not be {@code null} 329 * 330 * @return an appropriate {@link Annotation}, or {@code null} 331 * 332 * @exception NullPointerException if {@code annotationType} is {@code null} 333 * 334 * @see AnnotatedConstruct#getAnnotation(Class) 335 * 336 * @deprecated The use of this method is discouraged. 337 */ 338 @Deprecated 339 @Override // AnnotatedConstruct 340 @SuppressWarnings({"try", "unchecked"}) 341 public final <A extends Annotation> A[] getAnnotationsByType(final Class<A> annotationType) { 342 if (!annotationType.isAnnotation()) { 343 return (A[])EMPTY_ANNOTATION_ARRAY; 344 } 345 final String canonicalName = annotationType.getCanonicalName(); 346 for (final AnnotationMirror a : this.getAnnotationMirrors()) { 347 if (((QualifiedNameable)a.getAnnotationType().asElement()).getQualifiedName().contentEquals(canonicalName)) { 348 // TODO: is this lock actually needed, given how delegateSupplier works? 349 try (var lock = this.domain().lock()) { 350 return this.delegate().getAnnotationsByType(annotationType); 351 } 352 } 353 } 354 return (A[])EMPTY_ANNOTATION_ARRAY; 355 } 356 357 @Override // Object 358 public final int hashCode() { 359 // Interesting; hashCode doesn't cause symbol completion. See: 360 // 361 // https://github.com/openjdk/jdk/blob/jdk-26%2B25/src/jdk.compiler/share/classes/com/sun/tools/javac/code/Type.java#L565-L568 362 // (AnnoConstruct doesn't define it so super.hashCode() is Object.hashCode()) 363 // 364 // https://github.com/openjdk/jdk/blob/jdk-26%2B25/src/jdk.compiler/share/classes/com/sun/tools/javac/code/Symbol.java 365 // (Symbol (Element) doesn't override it at all.) 366 return this.delegate().hashCode(); 367 } 368 369 @Override // Object 370 @SuppressWarnings("try") 371 public final String toString() { 372 String s = this.s; // volatile read 373 if (s == null) { 374 try (var lock = this.domain().lock()) { 375 s = this.s = this.delegate().toString(); // volatile write, read 376 } 377 } 378 return s; 379 } 380 381 382 /* 383 * Static methods. 384 */ 385 386 387 /** 388 * <dfn>Unwraps</dfn> the supplied {@link AnnotatedConstruct} implementation such that the returned value is not an 389 * instance of {@link UniversalConstruct}. 390 * 391 * @param <T> an {@link AnnotatedConstruct} subtype (possibly {@link UniversalElement} or {@link UniversalType}) 392 * 393 * @param t an {@link AnnotatedConstruct}; may be {@code null} 394 * 395 * @return an object of the appropriate type that is guaranteed not to be an instance of {@link UniversalConstruct} 396 * 397 * @see #delegate() 398 */ 399 @SuppressWarnings("unchecked") 400 public static final <T extends AnnotatedConstruct> T unwrap(T t) { 401 while (t instanceof UniversalConstruct<?> uc) { 402 t = (T)uc.delegate(); 403 } 404 return t; 405 } 406 407}