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.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.ArrayList;
024import java.util.List;
025import java.util.Optional;
026
027import java.util.function.Supplier;
028
029import javax.lang.model.AnnotatedConstruct;
030
031import javax.lang.model.element.AnnotationMirror;
032import javax.lang.model.element.Element;
033
034import javax.lang.model.type.TypeMirror;
035
036import org.microbean.construct.constant.Constables;
037
038import org.microbean.construct.element.SyntheticAnnotationMirror;
039import org.microbean.construct.element.UniversalAnnotation;
040import org.microbean.construct.element.UniversalElement;
041
042import org.microbean.construct.type.UniversalType;
043
044import static java.lang.constant.ConstantDescs.BSM_INVOKE;
045import static java.lang.constant.ConstantDescs.CD_List;
046
047import static java.lang.constant.MethodHandleDesc.ofConstructor;
048
049import static java.util.Collections.unmodifiableList;
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 * @param <U> a type representing one of the permitted subclasses
060 *
061 * @author <a href="https://about.me/lairdnelson" target="_top">Laird Nelson</a>
062 *
063 * @see AnnotatedConstruct
064 *
065 * @see UniversalElement
066 *
067 * @see UniversalType
068 */
069public abstract sealed class UniversalConstruct<T extends AnnotatedConstruct, U extends UniversalConstruct<T, U>>
070  implements AnnotatedConstruct, Constable
071  permits UniversalElement, UniversalType {
072
073
074  /*
075   * Instance fields.
076   */
077
078
079  // Retained only for Constable implementation
080  private final PrimordialDomain domain;
081
082  // Eventually this should become a lazy constant/stable value
083  // volatile not needed
084  private Supplier<? extends T> delegateSupplier;
085
086  // Eventually this should become a lazy constant/stable value
087  private volatile String s;
088
089  // Eventually this should become a lazy constant/stable value
090  private volatile List<? extends UniversalAnnotation> annotations;
091
092  private final List<? extends UniversalAnnotation> syntheticAnnotations;
093
094
095  /*
096   * Constructors.
097   */
098
099
100  /**
101   * Creates a new {@link AnnotatedConstruct}.
102   *
103   * @param delegate a delegate to which operations are delegated; must not be {@code null}
104   *
105   * @param domain a {@link PrimordialDomain} representing the construct domain from which the supplied {@code
106   * delegate} is presumed to have originated; must not be {@code null}
107   *
108   * @exception NullPointerException if either argument is {@code null}
109   *
110   * @see #UniversalConstruct(AnnotatedConstruct, List, PrimordialDomain)
111   */
112  protected UniversalConstruct(final T delegate, final PrimordialDomain domain) {
113    this(delegate, null, domain);
114  }
115
116  /**
117   * Creates a new {@link AnnotatedConstruct}.
118   *
119   * @param delegate a delegate to which operations are delegated; must not be {@code null}
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   *
125   * @param domain a {@link PrimordialDomain} representing the construct domain from which the supplied {@code
126   * delegate} is presumed to have originated; must not be {@code null}
127   *
128   * @exception NullPointerException if any argument is {@code null}
129   *
130   * @see #annotate(List)
131   */
132  @SuppressWarnings("try")
133  protected UniversalConstruct(final T delegate,
134                               final List<? extends AnnotationMirror> annotations,
135                               final PrimordialDomain domain) {
136    super();
137    this.domain = requireNonNull(domain, "domain");
138    if (annotations == null) {
139      this.syntheticAnnotations = List.of();
140    } else if (annotations.isEmpty()) {
141      this.syntheticAnnotations = List.of();
142      this.annotations = List.of(); // volatile write
143    } else {
144      final List<UniversalAnnotation> delegateAnnotations = new ArrayList<>(annotations.size());
145      final List<UniversalAnnotation> syntheticAnnotations = new ArrayList<>(annotations.size());
146      for (final AnnotationMirror annotation : annotations) {
147        switch (annotation) {
148        case null -> throw new IllegalArgumentException("annotations: " + annotations);
149        case SyntheticAnnotationMirror sam -> syntheticAnnotations.add(UniversalAnnotation.of(sam, domain));
150        case UniversalAnnotation ua -> {
151          switch (ua.delegate()) {
152          case SyntheticAnnotationMirror sam -> syntheticAnnotations.add(ua);
153          case UniversalAnnotation x -> throw new AssertionError();
154          default -> delegateAnnotations.add(UniversalAnnotation.of(annotation, domain));
155          }
156        }
157        default -> delegateAnnotations.add(UniversalAnnotation.of(annotation, domain));
158        }
159      }
160      this.annotations = delegateAnnotations.isEmpty() ? List.of() : unmodifiableList(delegateAnnotations); // volatile write
161      this.syntheticAnnotations = syntheticAnnotations.isEmpty() ? List.of() : unmodifiableList(syntheticAnnotations);
162    }
163    final T unwrappedDelegate = unwrap(requireNonNull(delegate, "delegate"));
164    if (unwrappedDelegate == delegate) {
165      this.delegateSupplier = () -> {
166        try (var lock = domain.lock()) {
167          // No unwrapping happened so do symbol completion early; most common case.
168          if (unwrappedDelegate instanceof Element) {
169            ((Element)unwrappedDelegate).getModifiers();
170          } else {
171            ((TypeMirror)unwrappedDelegate).getKind();
172          }
173          this.delegateSupplier = () -> unwrappedDelegate;
174        }
175        return unwrappedDelegate;
176      };
177    } else {
178      assert delegate instanceof UniversalConstruct<?, ?>;
179      // Symbol completion already happened because unwrapping actually happened
180      this.delegateSupplier = () -> unwrappedDelegate;
181    }
182  }
183
184
185  /*
186   * Instance methods.
187   */
188
189
190  /**
191   * <strong>Experimental</strong>; returns a new {@link UniversalConstruct} instance annotated with only the supplied
192   * annotations, which are often synthetic.
193   *
194   * @param replacementAnnotations a {@link List} of {@link AnnotationMirror}s; must not be {@code null}
195   *
196   * @return a new {@link UniversalConstruct} instance; never {@code null}
197   *
198   * @exception NullPointerException if {@code replacementAnnotations} is {@code null}
199   */
200  // Experimental. Unsupported.
201  protected abstract U annotate(final List<? extends AnnotationMirror> replacementAnnotations);
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<? extends ConstantDesc> describeConstable() {
222    final T delegate = this.delegate();
223    final List<? extends UniversalAnnotation> annotations = this.annotations; // volatile read; may be null and that's OK
224    return this.domain() instanceof Domain d ? Constables.describe(delegate, d)
225      .flatMap(delegateDesc -> Constables.describe(annotations, Constable::describeConstable)
226               .map(annosDesc -> DynamicConstantDesc.of(BSM_INVOKE,
227                                                        ofConstructor(ClassDesc.of(this.getClass().getName()),
228                                                                      ClassDesc.of(delegate instanceof TypeMirror ? TypeMirror.class.getName() : Element.class.getName()),
229                                                                      CD_List,
230                                                                      ClassDesc.of(PrimordialDomain.class.getName())),
231                                                        delegateDesc,
232                                                        annosDesc,
233                                                        ((Constable)d).describeConstable().orElseThrow()))) :
234      Optional.empty();
235  }
236
237  /**
238   * Returns the {@link PrimordialDomain} supplied at construction time.
239   *
240   * @return the non-{@code null} {@link PrimordialDomain} supplied at construction time
241   */
242  public final PrimordialDomain domain() {
243    return this.domain;
244  }
245
246  @Override // Object
247  public final boolean equals(final Object other) {
248    // Interesting; equality does not cause symbol completion. See:
249    //
250    // https://github.com/openjdk/jdk/blob/jdk-26%2B25/src/jdk.compiler/share/classes/com/sun/tools/javac/code/Type.java#L553-L559
251    // (the only type that overrides this is ArrayType; see
252    // https://github.com/openjdk/jdk/blob/jdk-26%2B25/src/jdk.compiler/share/classes/com/sun/tools/javac/code/Type.java#L1402-L1406)
253    //
254    // https://github.com/openjdk/jdk/blob/jdk-26%2B25/src/jdk.compiler/share/classes/com/sun/tools/javac/code/Symbol.java
255    // (Symbol (Element) doesn't override it at all.)
256    return this == other || switch (other) {
257    case null -> false;
258    case UniversalConstruct<?, ?> uc when this.getClass() == uc.getClass() -> this.delegate().equals(uc.delegate());
259    default -> false;
260    };
261  }
262
263  @Override // AnnotatedConstruct
264  @SuppressWarnings("try")
265  public final List<? extends UniversalAnnotation> getAnnotationMirrors() {
266    List<? extends UniversalAnnotation> annotations = this.annotations; // volatile read
267    if (annotations == null) {
268      try (var lock = this.domain().lock()) {
269        annotations =
270          this.annotations = UniversalAnnotation.of(this.delegate().getAnnotationMirrors(), this.domain()); // volatile read/write
271      }
272      assert annotations != null;
273    }
274    if (annotations.isEmpty()) {
275      return this.syntheticAnnotations;
276    } else if (this.syntheticAnnotations.isEmpty()) {
277      return annotations;
278    } else {
279      final List<UniversalAnnotation> rv = new ArrayList<>(annotations);
280      rv.addAll(this.syntheticAnnotations);
281      return unmodifiableList(rv);
282    }
283  }
284
285  @Override // AnnotatedConstruct
286  @SuppressWarnings("try")
287  public final <A extends Annotation> A getAnnotation(final Class<A> annotationType) {
288    // TODO: is this lock actually needed, given how delegateSupplier works?
289    try (var lock = this.domain().lock()) {
290      return this.delegate().getAnnotation(annotationType);
291    }
292  }
293
294  @Override // AnnotatedConstruct
295  @SuppressWarnings("try")
296  public final <A extends Annotation> A[] getAnnotationsByType(final Class<A> annotationType) {
297    // TODO: is this lock actually needed, given how delegateSupplier works?
298    try (var lock = this.domain().lock()) {
299      return this.delegate().getAnnotationsByType(annotationType);
300    }
301  }
302
303  @Override // Object
304  public final int hashCode() {
305    // Interesting; hashCode doesn't cause symbol completion. See:
306    //
307    // https://github.com/openjdk/jdk/blob/jdk-26%2B25/src/jdk.compiler/share/classes/com/sun/tools/javac/code/Type.java#L565-L568
308    // (AnnoConstruct doesn't define it so super.hashCode() is Object.hashCode())
309    //
310    // https://github.com/openjdk/jdk/blob/jdk-26%2B25/src/jdk.compiler/share/classes/com/sun/tools/javac/code/Symbol.java
311    // (Symbol (Element) doesn't override it at all.)
312    return this.delegate().hashCode();
313  }
314
315  @Override // Object
316  @SuppressWarnings("try")
317  public final String toString() {
318    String s = this.s; // volatile read
319    if (s == null) {
320      try (var lock = this.domain().lock()) {
321        s = this.s = this.delegate().toString(); // volatile write, read
322      }
323      assert s != null;
324    }
325    return s;
326  }
327
328
329  /*
330   * Static methods.
331   */
332
333
334  /**
335   * <dfn>Unwraps</dfn> the supplied {@link AnnotatedConstruct} implementation such that the returned value is not an
336   * instance of {@link UniversalConstruct}.
337   *
338   * @param <T> an {@link AnnotatedConstruct} subtype (possibly {@link UniversalElement} or {@link UniversalType})
339   *
340   * @param t an {@link AnnotatedConstruct}; may be {@code null}
341   *
342   * @return an object of the appropriate type that is guaranteed not to be an instance of {@link UniversalConstruct}
343   *
344   * @see #delegate()
345   */
346  @SuppressWarnings("unchecked")
347  public static final <T extends AnnotatedConstruct> T unwrap(T t) {
348    while (t instanceof UniversalConstruct<?, ?> uc) {
349      t = (T)uc.delegate();
350    }
351    return t;
352  }
353
354}