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